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

4084 단어 윤성우CC

C++ 스타일의 초기화

우리는 이때까지 C스타일로 초기화를 진행해왔는데
C++스타일로는 다음과 같은 방식으로 초기화 및 선언이 가능하다.

C++에서는 위 두가지를 모두 지원한다!
그렇다면 이제 이야기를 객체의 생성으로 가보자!
그리고 다음 코드를 보자

이를 보면
main함수의 두 번째 문장은 객체의 생성 및 초기화를 연상시킨다.

실제로 이렇게 맴버 대 맴버로 복사가 일어난다.
그러므로 객체의 생성에도 이와 같이 이해할 수 있다.

SoSimple sim2 = sim1;
SoSimple sim2(sim1);

위 코드는 동일한 내용을 담고 있다.

그런데 한가지 이상한 생각이 들지 않는가?

C++의 모든 객체는 생성자의 호출을 동반한다고 했는데 sim2의 생성자 호출에 대해서는 언급한 바가 없다.

sim2는 과연 어떠한 과정을 거쳐서 생성되는 것일까?

SoSimple sim2(sim1);

SoSimple sim2(sim1);

이를 재분석 해보자.

  • SoSimple 형 객체를 생성해라.
  • 객체의 이름은 sim2로 정한다.
  • sim1을 인자로 받을 수 있는 생성자의 호출을 통해서 객체생성을 완료한다.


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

그리고 SoSimple sim2 = sim1; 도
묵시적으로 아래와 같이 변환되어
SoSimple sim2(sim1); 객체가 생성되는 것이다.

그런데!

앞서 정의한 SoSimple 클래스에는 이러한 유형의 생성자가 정의되어 있지않다!!!!

그렇다면 메인 함수가 이상한 것일까?
이에 대한 이해를 위해 우선 다음 예제를 살펴보자

그리고 난 후 "디폴트 복사 생성자"에 대한 이해를 통해서 이 부분의 답을 찾아보자.

ClassInit.cpp

#include <iostream>
using namespace std;

class SoSimple
{
private:
    int num1;
    int num2;
public:
    SoSimple(int n1,int n2):num1(n1),num2(n2)
    {
        
    }
    SoSimple(SoSimple &copy):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)
    cout<<"생성 및 초기화 직후"<<endl;
    sim2.ShowSimpleData();
    return 0;
}

위와 같은 생성자를 가리켜 복사 생성자라고한다.
이는 생성자의 정의형태가 독특해서 붙은 이름이 아니라 다만 이 생성자가 호출되는 시점이 다른 일반 생성자와 차이가 있기 때문에 붙은 것이다.

즉!!!

복사 생성자를 정확히 이해하기 위해서는 복사 생성자의 호출시점을 확실히 이해해야 한다 !

    SoSimple(const SoSimple &copy):num1(copy.num1),num2(copy.num2)
    {
        cout<<"Called SoSimple(SoSimple &copy)"<<endl;
    }

맴버대 맴버의 복사에 사용되는 원본을 변경시키는 것은 복사의 개념을 무너뜨리는 행위가 되니 const 키워드를 삽입 해서 이러한 실수를 막아 놓는 것이 좋다.

자동으로 삽입이 되는 디폴트 복사 생성자

우리는 복사 생성자의 삽입 없이도 맴버 대 맴버의 복사가 진행된 것을 경험하였다.

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

이렇듯 생성자가 존재하더라도, 복사 생성자가 정의되어 있지 않으면 디폴트 복사 생성자라는 것이 삽입되어 맴버 대 맴버의 복사를 진행한다.

따라서
아래의 이 둘은 완전 동일한것이라고 생각해도 된다 !

이와 같은 생각이 들 수 있다.

"맴버 대 맴버의 복사가 진행되는 디폴트 복사 생성자가 자동으로 삽입되니까, 굳이 복사 생성자를 직접 정의할 필요는 없겠네요 !!!"

실제로!
많은 경우에 있어서 복사 생성자를 직접 정의하지 않아도 된다.
그러나 반드시 복사 생성자를 정의 해야하는 경우가 있으나 이는 잠시후에 배우자 !

변환에 의한 초기화 ! 키워드 explict으로 막을 수 있다.


이러한 형태로 묵시적 변환이 일어난다고 앞서 배웠다.
그러나 !
explicit로 복사 생성자를 선언하면 위와같은 결과를 막아 대입 연산자를 이용한 객체의 생성 및 초기화는 불가능해진다.

묵시적 변환이 좋은 것만은 아니다.
그 이유는 explicit를 사용해서 코드의 명확함을 더해주는 것은 코드의 결과 예측에 도움이 된다.

좋은 웹페이지 즐겨찾기