본문 바로가기

Programming/ C++

(C++) 복사생성자 함수란 무엇일까?

728x90
반응형

복사생성자 함수란?

1. 필드 '값'을 모두 복사하는 것입니다.

2. 기본적으로 얕은 복사를 제공합니다.

3. 깊은 복사는 기본적으로 제공하지 않기 때문에 개발자가 필요에 의해

 제공해야합니다

 

==예시==

A(const A & aa){ //const를 써주는 이유는 복사할 대상의 값이 변경되면 안되기 때문

a = aa.a;

b = aa.b;

} //디폴트 기능(얕은 복사)

 

==복사생성자의 호출 시기==

1) 객체 생성시 객체를 인자로 줄 겨우

A aa; //생성자 호출

A bb(aa); //복사생성자 호출

 

2) 객체 생성시 객체를 대입할 경우

A aa; //생성자 호출

A aa = bb //복사생성자 호출

 

3) member function의 매개변수로 객체를 선언할 경우

void A : : disp(A aa)

 

4) 멤버함수에서 객체를 리턴할 경우

A getObject(  )

{

return 객체; //복사생성자 호출 

}

 

=========================== 

 얕은 복사란?

 =>명시적으로 만들지 않았을 때 기본적으로 제공되는 복사입니다.

1. (얕은 복사 예제 코드)

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
36
37
38
39
40
41
42
43
44
45
46
#include<iostream>
using namespace std;
 
class A
{
    int a;
    int b;
public :
    A(int a = 0int b = 0// C++에서 this 는 포인터, method 안의 

              //instance method 안에 있다.(this 선언할 수 없음, 항상 존재)

    {
        this->= a;
        this->= b;
 
    }
    A(const A & aa) { //복사생성자 ==> 명시적으로 만들면 디폴트기능이 없어짐.
        cout << "복사생성자" << endl;
        a = aa.a;
        b = aa.b;
    } //디폴트 기능(얕은 복사)
    void setA(int a) {
        this->= a;
}
    void setB(int b){
        this->= b;
    }
 
    int getA() {
        return a;
    }
    int getB() {
        return b;
    }
};
 
void main()
{
    A bb(1020);
    A aa;
 
    cout << aa.getA() << "\t" << aa.getB() << endl// 0, 0
    cout << bb.getA() << "\t" << bb.getB() << endl// 10, 20
    
    cout << "\n";
    A cc(bb); //복자생성자호출
    cout << cc.getA() << "\t" << cc.getB() << endl;  // bb값을 전체 카피
}
cs

※얕은 복사는 명시적으로 만들면 디폴트 기능이 없어집니다. 

(실행 결과)

=========================== 

 은 복사란?

 => 필요에 의해 개발자가 직접 복사생성자함수를 만들어 사용하는 것. 

2. (깊은 복사 예제 코드)

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
36
37
38
#include<iostream>
using namespace std;
 
class A
{
    int *p;
public:
    A(int i = 0)
    {
        p = new int;
        *= i;
    }
    /*
    A(const A& aa)
    {
        p= aa.p;
    } //복사생성자 default 모양
    */
    A(const A& aa)
    {
        p = new int;
        *= *aa.p;
    }
    ~A() {
        //cout << *p << endl;
        delete p;
    }
    int getP(){
        return *p;
    }
};
 
void main() { //정적메모리 stack구조 FILO(First In Last Out)
    A aa(10);
    //A bb(20);
    A bb(aa); //복사생성자 호출
    cout << bb.getP() << endl;
}
cs

만약 깊은 복사를 하지 않게 된다면, 런타임 오류가 발생하게 됩니다.

 현재 생성자를 포인터형식으로 만들었습니다.

 메인함수에서 A aa(10)을 호출하게 되면 힙(heap)영역에 10이라는 공간을 가리키는 포인터가 만들어집니다.

 그 후, A bb(aa)에서 복사생성자를 호출하게 되는데 이 때 힙(heap)영역의 10의 값을 복사하는게 아니고 10공간의 주소를 복사하게 되어 힙(heap)영역의 10의 공간을 같이 가리키게 됩니다. 그 후, 메인이 끝나며 메모리가 제거되는데 스택(FILO=>First In Last Out)에 의해 a bb(aa)부터 메모리를 해제하게 됩니다. 이 때, 먼저 10의 공간을 삭제해버리게 되고, A aa(10)의 메모리를 해제하려는데 이미 A bb(aa)에서 가리키는 곳을 지웠기 때문에, A aa(10)이 지울 공간이 사라져 런타임 오류가 발생하게 됩니다.

 

(실행 결과)

 

※ 필드를 포인터로 써줬을 경우엔 !반드시! 깊은 복사를 해야합니다. 그렇지 않으면 runtime 오류가 발생합니다.

반응형