6 장복사생성자 객체의생성과대입객체의값에의한전달복사생성자디폴트복사생성자복사생성자의재정의객체의값에의한반환임시객체 C++ 프로그래밍입문
1. 객체의생성과대입 int 형변수 : 선언과동시에초기화하는방법 (C++) int a = 3; int a(3); // 기본타입역시클래스와같이처리가능 객체의생성 ( 복습 ) class CPoint private : int x, y; public : CPoint(int a) : x(a), y(a) void Print() cout << "(" << x << ", " << y << ")" << endl; ; int main(void) CPoint P1(3); CPoint P2 = CPoint(4); CPoint P3 = 5; P1.Print(); P2.Print(); P3.Print(); 일반적방법 객체배열생성시주로사용 CPoint(5) 로형변환후초기화 return 0; 6 장복사생성자 1
1. 객체의생성과대입 복사생성과대입 class CPoint private : int x, y; public : CPoint(int a) : x(a), y(a) void Print() cout << "(" << x << ", " << y << ")" << endl; ; int main(void) CPoint P1(3); // 객체생성, P1 : (3, 3) CPoint P2(4); // 객체생성, P2 : (4, 4) CPoint P3 = P2; // 복사생성, P3 : (4, 4) CPoint P4(P2); // 복사생성, P4 : (4, 4) P1 = P2; // 객체대입, P1 : (4, 4) P1.Print(); P2.Print(); P3.Print(); P4.Print(); 객체생성과객체대입을구별하고객체생성중에서도일반생성과복사생성을구별하라. return 0; 6 장복사생성자 2
1. 객체의생성과대입 객체의복사생성과대입 복사생성 : 생성자 ( 그중에서복사생성자 ) 가동작함 대입 : 대입연산자가동작함복사생성과대입연산의디폴트동작 멤버단위복사! 디폴트동작방식은동일 멤버단위복사만으로충분한가? à 다음페이지예제 6 장복사생성자 3
1. 객체의생성과대입 예 : 문자열을다루기위한 CString 클래스구현 #include <iostream> #include <cstring> using namespace std; // strlen, strcpy 함수 class CString private : int len; char *str; // 문자열의길이 // 문자열포인터 public : CString(char *s = "Unknown") len = strlen(s); str = new char[len + 1]; strcpy(str, s); ~CString() delete [] str; void Print() cout << str << endl; ; str3 = str1 + str2; // 가능하려면? à 연산자오버로딩 (7 장 ) int main(void) CString str1 = "C++ Programming"; CString str2 = str1; // 복사생성 CString str3; 언제, 어디서에러가발생하는것일까? str3 = str1; str1.print(); str2.print(); str3.print(); return 0; // 대입연산 6 장복사생성자 4
1. 객체의생성과대입 CString 클래스의문제점분석 CString str2 = str1; 의수행결과 str3 = str1; 의수행결과 모든객체의 *str 이동일한메모리주소를가리킴 à 함수종료시각객체에대한소멸자가수행된다면! 동일한주소에대한 delete 시도 à 에러발생! à 복사생성과대입연산모두문제! 6 장복사생성자 5 해결방안 : 복사생성자 6.3~6.5 절, 대입 7.10 절
2. 객체의값에의한전달 객체의값에의한전달 : 복사생성발생 class CPoint private : int x, y; public : CPoint(int a = 0, int b = 0) : x(a), y(b) void Print() cout << "(" << x << ", " << y << ")" << endl; ; void ShowPoint(CPoint Po) Po.Print(); int main(void) CPoint P1(1, 2); ShowPoint(P1); // 값에의한객체전달 // 값에의한객체전달 return 0; 6 장복사생성자 6
2. 객체의값에의한전달 예 : CString 클래스객체의값에의한전달 : 문제점은? class CString private : int len; char *str; // 문자열의길이 // 문자열포인터 void ShowString(CString str) // 값에의한전달 str.print(); public : CString(char *s = "Unknown") len = strlen(s); str = new char[len + 1]; strcpy(str, s); ~CString() delete [] str; void Print() cout << str << endl; ; int main(void) CString str1 = "C++ Programming"; ShowString(str1); return 0; 1. str의소멸자수행 2. str1의소멸자수행 à 에러 6 장복사생성자 7
3. 복사생성자 복사생성자 복사생성시호출되는특수한생성자 복사생성자의모양유추 일반생성자 : CPoint P1(3, 4); à CPoint(int a, int b); 복사생성자 : CPoint P2(P1); à CPoint(CPoint Po); // ok??? ü 문제점 : 복사생성을위해 P1 을매개변수로전달시또다사복사생성발생 복사생성이무한히반복수행됨 6 장복사생성자 8
3. 복사생성자 복사생성자의모양 CPoint(CPoint &Po); // 참조에의한전달! CPoint(const CPoint &Po); // 실매개변수에대한변경방지 class CPoint private : int x, y; public : CPoint(const CPoint &Po) x = Po.x; y = Po.y; // 복사생성자 // 일반생성자 CPoint(const CPoint &Po, int a) x = Po.x * a; y = Po.y * a; CPoint(int a = 0, int b = 0) : x(a), y(b) // 일반생성자 void Print() cout << "(" << x << ", " << y << ")" << endl; ; int main(void) CPoint P1(1, 2); CPoint P2(P1); CPoint P3(P1, 3); P1.Print(); P2.Print(); P3.Print(); 복사생성 일반생성 return 0; 6 장복사생성자 9
4. 디폴트복사생성자 자동으로생성되는멤버함수 디폴트생성자 : 4.7 절 디폴트소멸자 : 4.7 절 디폴트복사생성자 : 본절 디폴트대입연산자 : 7.10 절 디폴트복사생성자 멤버단위복사 예 : CPoint 복사생성자를명시적으로만드는경우 디폴트복사생성자사라짐 디폴트생성자역시사라짐 CPoint(const CPoint &Po) x = Po.x; y = Po.y; 6 장복사생성자 10
5. 복사생성자의재정의 다음코드에대한올바른동작? CString str1 = "C++ Programming"; CString str2 = str1; 디폴트복사생성자를사용하는경우소멸자호출시에러발생 원하는동작 à 이에맞게복사생성자재정의! 6 장복사생성자 11
5. 복사생성자의재정의 class CString private : int len; char *str; CString 클래스에대한복사생성자재정의 복사생성자 public : CString(const CString &string) len = string.len; str = new char[len + 1]; strcpy(str, string.str); CString(char *s = "Unknown") len = strlen(s); str = new char[len + 1]; strcpy(str, s); ~CString() delete [] str; void Print() cout << str << endl; ; 일반생성자 // 값에의한전달, 복사생성 void ShowString(CString str) str.print(); int main(void) CString str1 = "C++ Programming"; CString str2 = str1; // 복사생성 str1.print(); ShowString(str2); return 0; // 값에의한전달 6 장복사생성자 12
6. 객체의값에의한반환 복사생성자가호출되는경우 객체의선언및초기화 : CPoint P2(P1); 객체의값에의한전달 : void ShowString(CString str) 객체의값에의한반환 : CString GetPoint() return str; CPoint 객체의값에의한반환예 class CPoint private : int x, y; public : CPoint(int a = 0, int b = 0) : x(a), y(b) void Print() cout << "(" << x << ", " << y << ")" << endl; ; CPoint GetPoint(void) CPoint Po(3, 4); return Po; int main(void) CPoint P1 = GetPoint(); P1.Print(); return 0; // 지역객체 Po 생성 // 지역객체값반환 // GetPoint 함수호출 6 장복사생성자 13
6. 객체의값에의한반환 CPoint 객체의반환예의동작원리 6 장복사생성자 14
6. 객체의값에의한반환 CString 객체의반환예 class CString private : int len; char *str; public : CString(char *s = "Unknown") len = strlen(s); str = new char[len + 1]; strcpy(str, s); ~CString() delete [] str; void Print() cout << str << endl; ; CString GetString(void) CString str("current String"); return str; return 0; // 객체생성 // 객체값반환, 임시객체생성 왜에러가발생하는것일까? à 객체반환에따른복사생성 int main(void) CString str1 = GetString(); str1.print(); 6 장복사생성자 15
6. 객체의값에의한반환 CString 객체반환시에러발생원인 지역객체인 str 과임시객체가동일한메모리를가리킴 str 에대한소멸자호출후임시객체에대한소멸자호출시에러발생! 6 장복사생성자 16
6. 객체의값에의한반환 CString 객체반환예의문제점해결 복사생성자만으로 ok! CString(const CString &string) len = string.len; str = new char[len + 1]; strcpy(str, string.str); 6 장복사생성자 17
7. 임시객체 임시객체 (temporary object) 의사용예 class CPoint private : int x; int y; public : CPoint(const CPoint &Po) : x(po.x), y(po.y) cout << " 복사생성자 : " << x << ", " << y << endl; CPoint(int a = 0, int b = 0) : x(a), y(b) cout << " 생성자 1 : " << x << ", " << y << endl; CPoint(const CPoint &Po, int a, int b) x = Po.x + a; y = Po.y + b; cout << " 생성자 2 : " << x << ", " << y << endl; ~CPoint() cout << " 소멸자 : " << x << ", " << y << endl; void Print() cout << "(" << x << ", " << y << ")" << endl; ; CPoint GetPoint(CPoint Po) return CPoint(Po, 2, 2); // 임시객체생성및반환 6 장복사생성자 18
7. 임시객체 코드계속 int main(void) 명시적으로임시객체를만드는방법 à CPoint(1, 1) 임시객체는왜사라지지않는것일까? à 컴파일러의존적 : 형식매개변수인 Po 가임시객체를그대로사용하면됨 : GetPoint 함수에서 CPoint 객체가반환되는과정에서도똑같은원리가적용됨 à P1 은임시객체를그대로사용! CPoint P1 = GetPoint(CPoint(1, 1)); // GetPoint 함수호출 CPoint P2 = CPoint(100, 100); // 임시객체생성, P2 초기화 CPoint &P3 = CPoint(200, 200); // 임시객체생성, P3이참조 CPoint P4; // 일반생성 P4 = CPoint(300, 300); // 임시객체생성및대입 P1.Print(); 사실은앞서 CString 객체의반환예에서도 P2.Print(); CString str1 = GetString(); 의결과로 P3.Print(); 임시객체는소멸되지않고그대로 P4.Print(); str1으로사용됨 CPoint(300, 300).Print(); // 임시객체생성 & 멤버함수호출 cout << " 프로그램종료 " << endl; return 0; 임시객체의사용원리 - 필요한곳에서임시객체생성가능 : 명시적생성또는묵시적생성 - 임시객체의생성주기는임시객체가필요한기간과일치 6 장복사생성자 19