내용 출처: 문톰의 긍정코딩님
동적 할당된 멤버 리소스를 동시에 제거해 줄 필요 있음
class Object {
~Object() {
cout << "Object 소멸!" << endl;
}
}
다른 객체를 사용해(인자로 받아) 객체 생성 시 호출
Object(const Object& obj);
복사 생성자 정의 없을 경우 자동 생성
class SimpleObject {
int id;
char* name;
static int objectCount; // 생성된 객체 수
static int idCounter; // 고유 ID 생성용 카운터
public:
SimpleObject(); // 기본 생성자
SimpleObject(const char* name); // 매개변수가 있는 생성자
**SimpleObject(const SimpleObject& obj); // 복사 생성자
SimpleObject& operator=(const SimpleObject& obj); // 복사 대입 연산자**
static int getObjectCount();
void printInfo() const;
~SimpleObject();
};
int SimpleObject::objectCount = 0;
int SimpleObject::idCounter = 0;
int SimpleObject::getObjectCount()
{
return objectCount;
}
void SimpleObject::printInfo() const
{
// 메서드 뒤 const: 이 메서드가 객체의 멤버를 수정하지 않음을 나타냄
cout << "SimpleObject [ID: " << id << ", Name: " << name << "]" << endl;
}
SimpleObject::SimpleObject() : SimpleObject("Default")
{
}
SimpleObject::SimpleObject(const char* name)
{
objectCount++;
this->id = idCounter++;
this->name = new char[strlen(name) + 1];
strcpy(this->name, name);
cout << "[" << id << "] SimpleObject " << name <<" Constructor Called!" << endl;
}
**SimpleObject::SimpleObject(const SimpleObject& obj)
{
objectCount++;
this->id = idCounter++;
//obj.id = 3; const 인자의 멤버 수정 불가
this->name = new char[strlen(obj.name) + 1];
strcpy(this->name, obj.name);
// 명시적 복사 생성자 정의가 없으면 컴파일러가 자동으로 생성
// 자동 복사 생성자는 멤버별 얕은 복사 수행; name의 메모리 주소만 복사
cout << "[" << id << "] SimpleObject " << name <<" Copy Constructor Called!" << endl;
}
SimpleObject &SimpleObject::operator=(const SimpleObject &obj)
{
if (this == &obj) return *this;
delete[] this->name;
this->name = new char[strlen(obj.name) + 1];
strcpy(this->name, obj.name);
cout << "[" << id << "] SimpleObject " << name <<" Copy Assignment Operator Called!" << endl;
return *this;
}**
SimpleObject::~SimpleObject()
{
cout << "[" << id << "] SimpleObject " << name <<" Destructor Called!" << endl;
delete[] name;
objectCount--;
}
void test_simple_object()
{
SimpleObject obj1;
auto ptr = new SimpleObject("Dynamic Object");
SimpleObject obj2("Object 2");
delete ptr;
SimpleObject obj4 = obj2; // 복사 생성자 호출
obj2 = obj1; // 복사 대입 연산자 호출
cout << "End of test_simple_object()" << endl;
}
[0] SimpleObject Default Constructor Called!
[1] SimpleObject Dynamic Object Constructor Called!
[2] SimpleObject Object 2 Constructor Called!
[1] SimpleObject Dynamic Object Destructor Called!
[3] SimpleObject Object 2 Copy Constructor Called!
[2] SimpleObject Default Copy Assignment Operator Called!
End of test_simple_object()
[3] SimpleObject Object 2 Destructor Called!
[2] SimpleObject Default Destructor Called!
[0] SimpleObject Default Destructor Called!Call by Value로 인자 전달 시 복사 생성자 호출
void SimpleObject::printInfo() const
{
// 메서드 뒤 const: 이 메서드가 객체의 멤버를 수정하지 않음을 나타냄
cout << "SimpleObject [ID: " << id << ", Name: " << name << "]" << endl;
}
**void getReferenceAndPrint(const SimpleObject& obj)
{
cout << "Inside getReferenceAndPrint()" << endl;
obj.printInfo();
}
void getValueAndPrint(SimpleObject obj)
{
cout << "Inside getValueAndPrint()" << endl;
obj.printInfo();
}**
void test_call_by_value()
{
SimpleObject obj1;
**getReferenceAndPrint(obj1); // 참조로 전달, 복사 생성자 호출 안 됨
getValueAndPrint(obj1); // 값으로 전달, 복사 생성자 호출 됨**
cout << "End of test_call_by_value()" << endl;
}
[0] SimpleObject Default Constructor Called!
Inside getReferenceAndPrint()
SimpleObject [ID: 0, Name: Default]
[1] SimpleObject Default Copy Constructor Called!
Inside getValueAndPrint()
SimpleObject [ID: 1, Name: Default]
[1] SimpleObject Default Destructor Called!
End of test_call_by_value()
[0] SimpleObject Default Destructor Called!참조 값이 아닌 객체 반환 시 복사 생성자가 호출
단, RVO(Return Value Optimization) 비활성화 되어있어야 함
**SimpleObject getNewObject(const char* name)
{
SimpleObject obj(name);
return obj; // 복사 생성자 호출; RVO(복사 생략 최적화)로 무시될 수 있음
}**
void test_return_by_value()
{
**SimpleObject obj1 = getNewObject("Returned Object");** // 복사 생성자 호출; RVO로 무시될 수 있음
// RVO 비활성화 옵션: g++ -fno-elide-constructors
cout << "End of test_return_by_value()" << endl;
}
-fno-elide-constructors 옵션 사용 시[0] SimpleObject Returned Object Constructor Called!
[1] SimpleObject Returned Object Copy Constructor Called!
[0] SimpleObject Returned Object Destructor Called!
[2] SimpleObject Returned Object Copy Constructor Called!
[1] SimpleObject Returned Object Destructor Called!
End of test_return_by_value()
[2] SimpleObject Returned Object Destructor Called! SimpleObject obj(name); getNewObject 스택에서 0번 객체 생성 - 일반 생성자 호출return obj; return을 통해 0번 객체를 복사하여 함수 바깥 스택(즉 test_return_by_value 스택)에 1번 객체 생성; 복사 생성자 호출SimpleObject obj1 = getNewObject("Returned Object");obj1 변수에 대입… 1번 객체 복사하여 2번 객체 생성; 복사 생성자 호출test_return_by_value 스택 종료; 2번 객체 소멸[0] SimpleObject Returned Object Constructor Called!
End of test_return_by_value()
[0] SimpleObject Returned Object Destructor Called! 복사 생성자/대입 연산자 private으로 선빵치기.
class Example_Private {
public:
Example_Private(int x);
private:
Example_Private(const Example_Private& other);
Example_Private& operator=(const Example_Private& other);
int value;
};
void example_test() {
Example_Private ex1(10);
// Example1 ex2 = ex1; // 오류: 복사 생성자가 private이므로 접근 불가
Example_Private ex3(20);
// ex3 = ex1; // 오류: 복사 대입 연산자가 private이므로 접근 불가
}
Example_Private::Example_Private(int x) : value(x) {}
// 복사 생성자는 private으로 선언되어 외부에서 접근 불가
Example_Private::Example_Private(const Example_Private& other) : value(other.value) {}
// 복사 대입 연산자도 private으로 선언되어 외부에서 접근 불가
Example_Private& Example_Private::operator=(const Example_Private& other)
{
if (this != &other) {
value = other.value;
}
return *this;
}
C++11부터의 기능; 해당 메서드가 필요치 않은 경우 기본 생성 메서드 삭제 가능
class Example_Delete {
public:
Example_Delete(int x);
// 복사 생성자와 복사 대입 연산자를 삭제
Example_Delete(const Example_Delete& other) = delete;
Example_Delete& operator=(const Example_Delete& other) = delete;
private:
int value;
};
void example_test() {
Example_Delete ex4(30);
// Example_Delete ex5 = ex4; // 오류: 복사 생성자가 삭제되었으므로 접근 불가
Example_Delete ex6(40);
// ex6 = ex4; // 오류: 복사 대입 연산자가 삭제되었으므로 접근 불가
}
Example_Delete::Example_Delete(int x) : value(x) {