C++ 스타일의 초기화


C++은 두가지 형태의 선언 및 초기화를 제공한다


int num =20;

int num(20);

int &ref=num;

int &ref(num);


이와같이 , 클래스를 선언및 초기화할때 멤버대 멤버 복사가 진행된다.


복사생성자


SoSimple sim1 (15,20);

SoSimple sim2 = sim1; // 멤버끼리의 얕은복사 , SoSimple sim2 =sim1 은 SoSimple sim2(sim1)으로 변환된다. ( 묵시적 변환 )


SoSimple sim2(sim1) 에 담겨있는 의미는 다음과 같다.


SoSimple 형 객체를 생성하라.

객체의 이름은 sim2로 한다.

sim1을 인자로 받을수 있는 생성자의 호출을 통해서 객체생성을 완료한다. ( 복사생성자 )


복수생성자를 따로 정의하지않으면 디폴트 복수생성자라는것이 자동으로 생성된다.


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) {} // 복사생성자, 정의하지않을시 디폴트 복사생성자 자동삽입

// 복사 생성자의 매개변수는 항상 참조형이여야한다 ( & ) . 참조형으로 선언하지않으면 복사생성자의 호출이 무한루프에 빠지기때문이다. 참조형으로 선언하지않으면 객체의 인자 전달시에도 복사생성자의 호출이 필요하기 때문이다. 

};


묵시적 변환이 일어나지않게 하고싶을때 : explicit 키워드


explicit SoSimple ( const SoSimple &copy) : num(copy,num1) , num2(copy.num2) {} 와 같은방식으로 복사생성자를 정의하면 묵시적 변환을 제한할 수 있다.  묵시적변환이 많이 발생하는 코드일수록 코드의 결과를 예측하기가 어려워지기때문에, explicit 키워드는 코드의 명확함을 더하기위해 자주 사용된다.


얕은복사와 깊은복사


디폴트 복사생성자는 멤버대 멤버의 복사를 진행하는데 , 이는 얕은복사이기때문에 멤버변수가 힙의 메모리 공간을 참조하는 경우에, 복사시 멤버변수가 같은 주소값을 참조하여 완벽하게 분리된 객체가 아니게되어 문제가 될 수 있다.


class Person

{

private :

char * name;

int age;

public :

Person(char * myname , int myage )

{

int len = strlen(myname)+1;

name = new char[len];

strcpy(name,myname);

age = myage;

}

~Person()

{

delete []name;

}

};


클래스가 이렇게 정의되어있을때 , 디폴트 복사생성자로 복사를 하게되면 (얕은복사) 복사한 객체와 원래 객체의 name 멤버가 가리키는 주소값이 동일하게되어 한 객체가 소멸될때 , name 값 또한 소멸하게되어 나머지 객체의 소멸자 호출시 delete []name; 에서 컴파일오류가 생성된다. 따라서 이럴땐 복사생성자를 깊은복사를 할 수 있게 정의해두어야한다.


Person(const Person &copy) : age(copy.age)

{

name = new char [strlen(copy.name)+1];

strcpy(name,copy.name);

}


복사 생성자의 호출시점


case1 : 기존에 생성된 객체를 이용해서 새로운 객체를 초기화하는 경우

case2 : Call - by - Value  방식의 함수호출 과정에서 객체를 인자로 전달하는 경우

case3 : 객체를 반환하되 , 참조형으로 반환하지 않는 경우


공통점 : 객체를 새로 생성해야하고 , 생성과 동시에 동일한 자료형의 객체로 초기화해야한다 !


메모리 공간의 할당과 초기화가 동시에 일어나는 상황


int num1=num2; // case1

SimpleFunc(num); // case2 , 호출

return n; // case3 - 함수가 값을 반환하면 별도의 메모리공간이 할당되고 이 공간에 반환값이 저장된다.


void SimpleFuncObj1 ( SoSimple ob )

{

ob.ShowData();

}

SoSimple SimpleFuncObj2 ( SoSimple ob )

{

return ob;

}

SoSimple sim2 =sim1;

SimpleFuncObj1(sim1); // case2 , 매개변수 ob에 sim1 전달시 복사생성자 호출

SimpleFuncObj2(sim1); // case2 , case3 둘다일어남 , return 으로 객체 반환시 임시객체라는것이 생성되고 임시객체의 참조값을 반환한다. 


임시객체는 참조자에 참조되지않는이상 다음 행으로 넘어가면 바로 소멸된다. ( 참조자로 참조하지않으면 다음행에서 접근할 수 없기때문 )







+ Recent posts