공부/C++

[C++] 5-1. 복사 생성자

knhoo 2024. 10. 30. 11:43
728x90

int obj1 = obj2;

---------------------------

int obj1;

obj1 = obj2;

복사 생성자가 필요한 이유 :

대입 연산 즉, 새로운 객체를 기존의 객체로 초기화하는 대입 연산의 결과를 정의하게끔 하도록 하기 위해서 만들어짐


C++ 스타일의 초기화

//C 스타일 초기화
int num = 20;
int &ref = num;

//C++ 스타일 초기화
int num(20);
int &ref(num);

 

 

복사 생성자를 정의하지 않으면, 멤버 대 멤버의 복사를 진행하는 디폴트 복사 생성자가 삽입된다.

class SoSimple
{
private:
	int num1;
    int num2;
public :
	SoSimple(int n1, int n2) : num1(n1), num2(n2)
    { }
    //==================삽입된 디폴트 복사 생성자====================
    SoSimple(const SoSimple &copy) : num1(copy.num1), num2(copy.num2)
    { }
    //===============================================================
    void ShowSimpleData()
    {
    	cout<<num1<<endl;
        cout<<num2<<endl;
    }
};

int main(void){
	SoSimple sim1(15,20);
    SoSimple sim2 = sim1; 
    //SoSimple (sim1,sim2)의 생성자 필요
    //해당 유형의 생성자가 정의되어 있지 않으면 자동으로 생성자가 삽입된다.
    //=> 복사 생성자
    //다른 생성자의 정의와 상관 없이,복사 생성자가 정의되어있지 않으면
    //디폴트 복사 생성자가 자동으로 삽입된다.
    
    sim2.ShowSimpleData();
    return 0;
}

다음 문장에서 sim2객체를 새로 생성해서, 객체 sim1과 sim2간의 멤버 대 멤버 복사가 일어난다.

SoSimple sim2 = sim1;

 

아래 문장들은 서로 동일한 의미로 해석된다.

int num1 = 2;

int num1(num2);

 

SoSimple sim2 = sim1;

SoSimple sim2(sim1);//대입 연산에서 일어나야 할 일들을 생성자를 통해 정의

 

SoSimple sim2(sim1);

SoSimple sim2(sim1);

위의 객체 생성문에서 호출하고자 하는 생성자는 다음과 같이 SoSimple객체를 인자로 받을 수 있는 생성자이다.

SoSimple(SoSimple &copy){

...

}

 

SoSimple sim2=sim1; 이 문장도 다음의 형태로 자동 변환되어서 객체가 생성되는 것이다.

SoSimple sim2(sim1);

 

 

SoSimple(const SoSimple &ref){ } //생성자

 

예제

#include <iostream>
using namespace std;

class SoSimple
{
private:
    int num1;
    int num2;
public:
    SoSimple(int n1, int n2)
        : num1(n1), num2(n2)
    {
        //empty
    }
    //==================삽입된 디폴트 복사 생성자====================
    SoSimple(const SoSimple& copy)//const : 대입 이후에 대입한 변수의 값이 변경되는 상황을 방지
        : num1(copy.num1), num2(copy.num2)
    {
        cout << "Called SoSimple(SoSimple &copy)" << endl;
    }
    //===============================================================
    void ShowSimpleData()
    {
        cout << num1 << endl;
        cout << num2 << endl;
    }
};

int main(void) {
    SoSimple sim1(15, 30);
    cout << "생성 및 초기화 직전" << endl;
    SoSimple sim2 = sim1;//SoSimple sim2(sim1);로 변환
    //SoSimple (sim1,sim2)의 생성자 필요
    //해당 유형의 생성자가 정의되어 있지 않으면 자동으로 생성자가 삽입된다.
    //=> 복사 생성자
    //다른 생성자의 정의와 상관 없이,복사 생성자가 정의되어있지 않으면
    //디폴트 복사 생성자가 자동으로 삽입된다.
    cout << "생성 및 초기화 직후" << endl;

    sim2.ShowSimpleData();
    return 0;
}

위 코드의 생성자를 '복사 생성자(copy constructor)'라고 부른다. 

복사 생성자는 다른 일반 생성자와 호출되는 시점이 다르다.


키워드 explicit

SoSimple sim2 = sim1; => SoSimple sim2(sim1);

C++에서의 이러한 묵시적 형 변환은 복사 생성자를 explicit으로 선언하면 막을 수 있다.

explicit SoSimple(const SoSimple &copy)
	: num1(copy.num1), num2(copy.num2)
{
	//empty!!
}

explicit은 묵시적 변환을 막아서 코드의 명확함을 더해준다.

 

복사 생성자뿐만 아니라, 전달 인자가 하나인 생성자에서도 묵시적 변환이 일어난다.

class AAA
{
private : 
	int num;
public :
	explicit AAA(int n) : num(n) { }
    ....
};

AAA생성자를 explicit로 설정하면 AAA obj = 3과 같은 형태로 객체 생성이 불가능하다.

AAA obj(3);의 형태로 객체를 생성해야만 한다.

728x90