본문 바로가기

C++

C++ 복사생성자

복사생성자 란?

자기 자신을 인자로 받는 생성자
자기 자신의 객체로 자기 자신을 초기화한다.

 

복사생성자의 호출 시점

1. 먼저 생성한 객체를 나중에 생성하는 객체의 인자로 전다하는 경우

2. 함수의 인자로 객체가 전달되는경우

3. 함수의 반환 값으로 객체가 반환되는 경우

 

복사생성자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 
class  AAA
{
public:
    int a;
    int b;
 
    AAA() {}
    ~AAA() {}
    AAA(int a, int b)
    {
        this->= a;
        this->= b;
    }
    AAA(AAA& aaa)
    {
        a = aaa.a;
        b = aaa.b;
    }
};
 
void main()
{
    AAA a1(10,20);
    AAA a2(a1);
    cout << a2.a << a2.b << endl;
}
cs

14행 : 복사 생성자 선언
16~17행 : 자기 자신의 변수값을 레퍼런스 aaa의 값으로 대입한다.
24행 : AAA의 복사 생성자 호출

해당 코드를 실행하면 결과로 10과 20이 출력되는 것을 확인할 수 있다.
물론 저기서 복사 생성자를 지워도 해당 코드는 문제없이 돌아간다.
그 이유는 복사 생성자를 구현하지 않으면 컴파일러는 자동적으로 디폴트 복사 생성자를 호출하게 되어있기 때문이다.

 

복사 생성자의 인자를 레퍼런스 타입으로 받는 이유
만약 복사 생성자의 인자 값이 레퍼런스가 아닌 일반 객체 타입이었다면 무한 루프에 빠지게 되는데
AAA a2(a1); 해당 문장은 컴파일러가 보면 AAA a2 = (AAA aaa = a1); 이런 식으로 된다.
이러면 aaa의 복사 생성자가 호출된다. 그러면 aaa = (AAA aaa = a1); 복사 생성자가 무한히 실행되는
무한 루프에 빠진다.

 

디폴트생성자의 문제점

위에서의 예제는 디폴트 생성자와 동일하다.

해당 복사 생성자는 문제가 하나 존재한다. 아래 예제는 해당 문제점을 보여주는 예시이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 
class  AAA
{
public:
    char* name;
    int age = 0;
 
    AAA(const char* name, int age) 
    {
        this->name = new char[strlen(name)+1];
        strcpy_s(this->name, strlen(name)+1, name);
        this->age = age;
    }
    ~AAA() {}
    AAA(AAA& aaa)
    {
        name = aaa.name;
        age = aaa.age;
    }
 
 
private:
 
};
 
void main()
{
    AAA a1("슬깃",20);
    AAA a2(a1);
    
    strcpy_s(a1.name, 10"무녀");
    a1.age = 15;
 
    cout << a1.name << a1.age << endl;
    cout << a2.name << a2.age << endl;
}
cs

14~18행 : 복사생성자

28행 : a1의 객체를 a2의 복사생성자를 호출하여 초기화 하였다.

30행 : a1의 name을 무녀로 변경

31행 : a1의 age를 15로 변경

33~34 : 출력

 

해당 예제를 출력하면
무녀 20
무녀 15
라는 값을 출력할 것이다.
분명히 우리는 a1의 값만 수정하였으나 a2의 name이 수정되었다.
이는 복사 생성자를 호출하면서 name의 메모리 주소를 a1과 a2가 같은 곳을 바라보게 되었기 때문이다.
이런 복사를 얕은 복사라고 부른다.
이를 해결하는 방법으로는 깊은 복사가 있다.
깊은 복사는 값을 복사할 때 메모리를 넘기는 방식이 아닌 값만 넘기고 메모리는 새롭게 만들어주는 방식이다.

 

깊은 복사

1
2
3
4
5
6
 
    AAA(AAA& aaa)
    {
        name = new char[strlen(aaa.name) + 1];
        strcpy_s(name, strlen(aaa.name) + 1, aaa.name);
        age = aaa.age;
    }
cs

위에 예제에서 변경되는곳은 복사생성자 부분이다.

얕은복사에서의 문제점이었던 같은 메모리를 사용하는 부분을 새롭게 동적할당 해주는걸로 해결할수있다.

 

'C++' 카테고리의 다른 글

C++ 함수객체  (0) 2020.09.23
C++ 상속  (0) 2020.09.17
C++ Static  (0) 2020.09.15
C++ Const  (0) 2020.09.14
C++ 조건부 컴파일  (0) 2020.09.07