[c++] 복사 생성자

조히·2023년 5월 6일
0

C++ 스타일의 초기화

C++의 선언과 초기화는
int num=20; int &ref=num;
도 가능하고
int num(20); int &ref(num);
도 가능함

마찬가지로
SoSimple sim2=sim1;
SoSimple sim2(sim1);과 같음
sim2 객체를 새로 생성해, 객체 sim1sim2간의 멤버 대 멤버 복사가 일어남

복사 생성자

C++의 모든 객체는 생성자의 호출을 동반하므로,
위 문장에서 sim1을 인자로 받을 수 있는 생성자의 호출을 통해 객체생성을 완료함
→ 이것이 복사 생성자

SoSimple(SoSimple &copy)
{
 	...
}

SoSimple sim2=sim1;SoSimple sim2(sim1);
으로 묵시적 변환이 되어 객체 생성

복사 생성자의 이름은 생성자가 호출되는 시점이 다른 일반 생성자와 차이가 있기 때문에 붙음

SoSimple(const SoSimple &copy)
{
 	...
}

이렇게 const 키워드를 삽입해 원본이 변경되는 것을 막는 것이 좋다
(이것이 일반적인 복사 생성자)

디폴트 복사 생성자

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

복사 생성자를 반드시 정의해야 하는 경우가 있음

키워드 explicit

복사 생성자의 묵시적 호출을 허용하지 않는 키워드 explicit

SoSimple sim2=sim1;SoSimple sim2(sim1);로 변환시키지 않음
→ 대입 연산자를 이용한 객체 생성 및 초기화 불가능

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

전달인자가 하나인 생성자에도 explicit 선언하면, 묵시적 변환을 허용하지 않음
ex) AAA obj=3 불가능, AAA obj(3)으로만 가능

깊은 복사와 얕은 복사

디폴트 복사 생성자는 멤버 대 멤버의 복사를 진행 → 얕은 복사
이는 멤버변수가 힙의 메모리 공간을 참조하는 경우에 문제가 생김

디폴트 복사 생성자의 문제점 : 동적 할당으로 멤버변수를 정의 했다면(char *과 같은), 디폴트 복사 생성자로 멤버 대 멤버 복사를 진행 했을 때 주소를 복사하기 때문에, 하나의 문자열을 두 개의 객체가 동시에 참조
한 객체의 소멸자에서 delete 연산을 하고, 다른 객체의 소멸자에서도 delete 연산을 하면 이미 지워진 문자열을 대상으로 delete 연산을 하기 때문에 문제 발생

깊은 복사를 위한 복사 생성자의 정의

객체 별로 각각의 문자열을 참조하게 만들면 소멸과정에서 문제가 발생하지 않음
→ 멤버뿐만 아니라 포인터로 참조하는 대상까지 깊게 복사 : 깊은 복사

Person(const Person& copy) : age(copy.age)
{
	name = new char[strlne(copy.name)+1];
    strcpy(name, copy.name);
}

→ 이 생성자가 하는 일

  • 멤버변수 age의 멤버 대 멤버 복사
  • 메모리 공간 할당 후 문자열 복사, 할당된 메모리 주소 값 멤버 name 저장

복사 생성자의 호출 시점

  • case 1 : 기존에 생성된 객체를 이용해 새로운 객체를 초기화 하는 경우
  • case 2 : Call by value 방식의 함수호출 과정에서 객체를 인자로 전달하는 경우
  • case 3 : 객체를 반환하되, 참조형으로 반환하지 않는 경우

메모리 공간의 할당과 초기화가 동시에 일어나는 상황 :
int num1=num2;
→ num1이라는 메모리 공간을 할당과 동시에 num2에 저장된 값으로 초기화

int SimpleFunc(int n)
{
	....
}
int main(void)
{
	int num=10;
    SimpleFunc(num);
}

→ 매개변수 n이 할당과 동시에 변수 num에 저장된 값으로 초기화

int SimpleFunc(int n)
{
	...
    return n;
}
int main(void)
{
	int num=10;
    cout<<SimpleFunc(num)<<endl;
    ...
}

→ 반환하는 순간 메모리 공간이 할당되면서 동시에 반환 값으로 초기화
변수에 저장하는 것과 별개로, 값을 반환하면 반환된 값은 별도의 메모리 공간이 할당되어 저장 됨

이는 객체도 마찬가지

할당 이후 복사 생성자를 통한 초기화

SoSimple SimpleFuncObj(SoSimple ob)
{
	return ob;
}

라는 함수가 있는데
SimpleFuncObj(obj); 가 호출될 경우

  1. ob 객체에 obj가 전달되며 ob의 복사 생성자 호출
  2. 임시객체에 ob가 전달되며 임시객체의 복사 생성자 호출
  3. 최종적으로 반환되는 객체는 임시객체,
    함수호출이 완료되면 지역적으로 선언된 객체 ob는 소멸되고 임시객체와 obj 객체만 남음

임시 객체

Temporary(200); 과 같이 값을 저장하지 않고 호출하면 임시객체 생성
임시객체가 생성된 위치에는 임시객체의 참조 값이 반환
Temporary(200).ShowTempInfo();이면
(임시객체의 참조 값).ShowTempInfo();와 같은 형태
→ 멤버함수의 호출 가능

참조 값이 반환되기 때문에
const Temporary &ref=Temporary(200);의 형태도 가능(상수 참조자)

  • 임시객체는 다음 행으로 넘어가면 바로 소멸
  • 참조자에 참조되는 임시객체는 바로 소멸되지는 않음

SoSimple tempRef=SimpleFuncObj(obj);
에서 SimpleFuncObj의 반환 값인 임시객체에 tempRef라는 이름을 할당해줌 (객체의 추가 생성 없이)

profile
Juhee Kim | Game Client Developer

0개의 댓글