C++ 복사 생성자(copy constructor)와 대입 연산자(substitution operator)
class의 데이터 멤버가 포인터인 경우 복사생성자, 대입연산자를 구현하여
deep copy 해주거나 사용하지 않을 경우 private로 막는다.
복사생성자, 대입연산자를 정의/구현하지 않은 경우 포인터가 아닌 멤버변수는 잘 복사됨.
1. 복사 생성자, 대입 연산자
미구현으로 문제가 될 현상
#include <iostream>
#include <string>
#include <tchar.h>
using namespace std;
class CSimple
{
public:
static int count;
CSimple()
{
count++;
print("constructor()");
}
~CSimple()
{
count--;
print("destroyer()");
}
CSimple(const CSimple &cls) //복사 생성자
{
print("copy constructor()");
}
CSimple& operator=(CSimple& cls) //대입 연산자
{
print("substitution operator()");
return *this; //자기자신을 돌려주어야 함.
}
void print(const string &str = "")
{
cout << "\t";
cout << " count : " << count;
if (str.size() != 0)
cout << " , " << str << endl;
}
};
int CSimple::count = 0;
CSimple func_bad(CSimple cls) {
return cls;
}
int main() {
cout << "CSimple cls1;" << endl;
CSimple cls1; //cls1.id = 1, constructor()
cout << endl;
cout << "CSimple cls2 = cls1;" << endl;
CSimple cls2 = cls1; //cls2.id = 2, copy constructor()
cout << endl;
cout << "CSimple cls3;" << endl;
CSimple cls3; //cls3.id = 2, constructor()
cout << endl;
cout << "cls3 = cls1;" << endl;
cls3 = cls1; //cls3.id = 1, cls1.id = 1, substitution operator()
cout << endl;
cout << "CSimple cls4 = func_bad(cls1);" << endl;
CSimple cls4 = func_bad(cls1);
cout << endl;
//cout << "CSimple cls5 = func_good(cls1);" << endl;
//CSimple cls5 = func_good(cls1);
//cout << endl;
cout << "CSimple* clsP = &cls1;" << endl;
cout << "cls1 = *clsP;" << endl;
CSimple* clsP = &cls1;
cls1 = *clsP; //가지자신을 대입
cout << endl;
cout << endl << endl << endl;
cout << "End Main()" << endl;
return 0;
}
<실행결과>
CSimple cls1;
count : 1 , constructor()
CSimple cls2 = cls1;
count : 1 , copy constructor()
CSimple cls3;
count : 2 , constructor()
cls3 = cls1;
count : 2 , substitution operator()
CSimple cls4 = func_bad(cls1);
count : 2 , copy constructor()
count : 2 , copy constructor()
count : 1 , destroyer()
CSimple* clsP = &cls1;
cls1 = *clsP;
count : 1 , substitution operator()
End Main()
count : 0 , destroyer()
count : -1 , destroyer()
count : -2 , destroyer()
count : -3 , destroyer()
계속하려면 아무 키나 누르십시오 . . .
:(
2. 복사 생성자, 대입 연산자 구현
#include <iostream>
#include <string>
#include <tchar.h>
using namespace std;
class CSimple
{
public:
int id;
static int count;
CSimple()
{
count++;
id = count;
print("constructor()");
}
~CSimple()
{
count--;
print("destroyer()");
}
CSimple(const CSimple &cls) //복사 생성자
{
/*
cls를 this로 depp copy 필요.
this에 메모리 할당된게 있다면 해재 한후 cls의 내용데로 new하여 내용 복사하도록 한다.
*/
count++;
id = count;
print("copy constructor()");
}
CSimple& operator=(CSimple& cls) //대입 연산자
{
if (this == &cls) //자기자신이면 복사하지 말기
{
cout << "\tsubstitution operator(), Don't copy onself" << endl;
return *this;
}
/*
cls를 this로 depp copy 필요.
this에 메모리 할당된게 있다면 해재 한후 cls의 내용데로 new하여 내용 복사하도록 한다.
*/
count++;
id = count;
print("substitution operator()");
return *this; //자기자신을 돌려주어야 함.
}
void print(const string &str = "")
{
cout << "\t";
cout << "class id : " << id;
cout << ", count : " << count;
if (str.size() != 0)
cout << " , " << str << endl;
}
};
int CSimple::count = 0;
// Pass and return BY VALUE:
CSimple func_bad(CSimple cls) {
//인자로 받는 cls에서 copy constructor() 호출됨. (불필요한 작업 발생)
//리턴되는 cls에서 copy constructor() 호출됨.
//인자로 받은 cls에서 destroyer() 호출됨. (불필요한 작업 발생)
return cls;
}
CSimple func_good(CSimple &cls) { //레퍼런스 사용
//리턴되는 cls에서 copy constructor() 호출됨.
return cls;
}
int main() {
cout << "CSimple cls1;" << endl;
CSimple cls1; //cls1.id = 1, constructor()
cout << endl;
cout << "CSimple cls2 = cls1;" << endl;
CSimple cls2 = cls1; //cls2.id = 2, copy constructor()
cout << endl;
cout << "CSimple cls3;" << endl;
CSimple cls3; //cls3.id = 2, constructor()
cout << endl;
cout << "cls3 = cls1;" << endl;
cls3 = cls1; //cls3.id = 1, cls1.id = 1, substitution operator()
cout << endl;
//cout << "CSimple cls4 = func_bad(cls1);" << endl;
//CSimple cls4 = func_bad(cls1);
//cout << endl;
cout << "CSimple cls5 = func_good(cls1);" << endl;
CSimple cls5 = func_good(cls1);
cout << endl;
cout << "CSimple* clsP = &cls1;" << endl;
cout << "cls1 = *clsP;" << endl;
CSimple* clsP = &cls1;
cls1 = *clsP; //가지자신을 대입
cout << endl;
cout << endl << endl << endl;
cout << "End Main()" << endl;
return 0;
}
<실행 결과>
CSimple cls1;
class id : 1, count : 1 , constructor()
CSimple cls2 = cls1;
class id : 2, count : 2 , copy constructor()
CSimple cls3;
class id : 3, count : 3 , constructor()
cls3 = cls1;
class id : 4, count : 4 , substitution operator()
CSimple cls5 = func_good(cls1);
class id : 5, count : 5 , copy constructor()
CSimple* clsP = &cls1;
cls1 = *clsP;
substitution operator(), Don't copy onself
End Main()
class id : 5, count : 4 , destroyer()
class id : 4, count : 3 , destroyer()
class id : 2, count : 2 , destroyer()
class id : 1, count : 1 , destroyer()
계속하려면 아무 키나 누르십시오 . . .
:D
안쓸꺼면 복사 생성자, 대입 연산자를 private로 막어버리는게 좋음.
3. 상속받은 child class 인경우 parent class의
복사 생성자, 대입 연산자를 명시적으로 호출해 준다.
#include <iostream>
#include <string>
#include <tchar.h>
using namespace std;
class CParent
{
public:
CParent() {};
~CParent() {};
CParent(const CParent&cls)
{
//deep copy
cout << "\tparent copy constructor()" << endl;
}
CParent& operator=(CParent& cls)
{
//deep copy
cout << "\tparent substitution operator()" << endl;
return *this;
}
};
class CSimple : public CParent
{
public:
int id;
static int count;
CSimple()
{
count++;
id = count;
print("constructor()");
}
~CSimple()
{
count--;
print("destroyer()");
}
CSimple(const CSimple &cls) : CParent(cls) //부모 복사 생성자 호출
{
/*
cls를 this로 depp copy 필요.
this에 메모리 할당된게 있다면 해재 한후 cls의 내용데로 new하여 내용 복사하도록 한다.
*/
count++;
id = count;
print("copy constructor()");
}
CSimple& operator=(CSimple& cls) //대입 연산자
{
if (this == &cls) //자기자신이면 복사하지 말기
{
cout << "\tsubstitution operator(), Don't copy onself" << endl;
return *this;
}
CParent::operator = (cls); //부모 대입 연산자 호출
/*
cls를 this로 depp copy 필요.
this에 메모리 할당된게 있다면 해재 한후 cls의 내용데로 new하여 내용 복사하도록 한다.
*/
count++;
id = count;
print("substitution operator()");
return *this; //자기자신을 돌려주어야 함.
}
void print(const string &str = "")
{
cout << "\t";
cout << "class id : " << id;
cout << ", count : " << count;
if (str.size() != 0)
cout << " , " << str << endl;
}
};
int CSimple::count = 0;
// Pass and return BY VALUE:
CSimple func_bad(CSimple cls) {
//인자로 받는 cls에서 copy constructor() 호출됨. (불필요한 작업 발생)
//리턴되는 cls에서 copy constructor() 호출됨.
//인자로 받은 cls에서 destroyer() 호출됨. (불필요한 작업 발생)
return cls;
}
CSimple func_good(CSimple &cls) {
//리턴되는 cls에서 copy constructor() 호출됨.
return cls;
}
int main() {
cout << "CSimple cls1;" << endl;
CSimple cls1; //cls1.id = 1, constructor()
cout << endl;
cout << "CSimple cls2 = cls1;" << endl;
CSimple cls2 = cls1; //cls2.id = 2, copy constructor()
cout << endl;
cout << "CSimple cls3;" << endl;
CSimple cls3; //cls3.id = 2, constructor()
cout << endl;
cout << "cls3 = cls1;" << endl;
cls3 = cls1; //cls3.id = 1, cls1.id = 1, substitution operator()
cout << endl;
//cout << "CSimple cls4 = func_bad(cls1);" << endl;
//CSimple cls4 = func_bad(cls1);
//cout << endl;
cout << "CSimple cls5 = func_good(cls1);" << endl;
CSimple cls5 = func_good(cls1);
cout << endl;
cout << "CSimple* clsP = &cls1;" << endl;
cout << "cls1 = *clsP;" << endl;
CSimple* clsP = &cls1;
cls1 = *clsP; //가지자신을 대입
cout << endl;
cout << endl << endl << endl;
cout << "End Main()" << endl;
return 0;
}
<실행 결과>
CSimple cls1;
class id : 1, count : 1 , constructor()
CSimple cls2 = cls1;
parent copy constructor()
class id : 2, count : 2 , copy constructor()
CSimple cls3;
class id : 3, count : 3 , constructor()
cls3 = cls1;
parent substitution operator()
class id : 4, count : 4 , substitution operator()
CSimple cls5 = func_good(cls1);
parent copy constructor()
class id : 5, count : 5 , copy constructor()
CSimple* clsP = &cls1;
cls1 = *clsP;
substitution operator(), Don't copy onself
End Main()
class id : 5, count : 4 , destroyer()
class id : 4, count : 3 , destroyer()
class id : 2, count : 2 , destroyer()
class id : 1, count : 1 , destroyer()
계속하려면 아무 키나 누르십시오 . . .
: )
'C++' 카테고리의 다른 글
ActiveX control (0) | 2010.11.04 |
---|---|
OLE (Object Linking and Embedding) (0) | 2010.11.04 |
Automation (자동화) (0) | 2010.11.02 |
COM (component object model) (1) | 2010.11.02 |
STL (Standard Template Library) (0) | 2010.11.01 |
C++ 파일과 콘솔 출력 예제 (0) | 2010.11.01 |
Registry 레지스트리 (0) | 2010.10.27 |
DLL (Dynamic Link Library) (0) | 2010.10.27 |