본문 바로가기
C++/기초(두들낙서)

[C++기초] 동적할당과 객체복사 연습문제

by Meaning_ 2022. 1. 17.
728x90
반응형

[문제] 다각형을 만드는 프로그램에 복사생성자,복사대입연산자, 이동생성자,이동대입연산자를 구현하여 

가 출력될 수 있게 해보자. 

 

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
 
#include <iostream>
 
using namespace std;
 
struct Point {
    int x, y;
};
 
class Polygon {
public:
    Polygon() {
        nPoints = 0;
        points = NULL;
    }
    Polygon(const int nPoints,const Point *points):nPoints(nPoints) {
        this->points = new Point[nPoints];
        for (int i = 0; i < nPoints; i++) {
            this->points[i] = points[i];
        }
    }
 
    //복사생성자 구현
 
    
    //복사대입연산자 구현 
 
 
    }
 
    ~Polygon() {
        delete[] points;
    }
 
    //이동 생성자구현
 
    
 
    //이동 대입 연산자 구현 
 
    
    int GetNPoints()const {
        return nPoints;
    }
 
    Point *GetPoints() const {
        if (nPoints == 0return NULL;
        return points;
    }
private:
    int nPoints;
    Point* points;
 
};
 
Polygon getSqaure() {
    Point points[4= { {0,0},{1,0},{1,1},{0,1} };
    Polygon p(4, points);
    return p;
}
 
int main() {
    Polygon a;
    a = getSqaure();//얕은 복사 2회 
    Polygon b (a);//깊은 복사 1회 
    Polygon c;
    c = a;//깊은 복사 1회 
 
    int nPoints = c.GetNPoints();
    Point* points = c.GetPoints();
    for (int i = 0; i < nPoints; i++) {
        cout << "( " << points[i].x << "," << points[i].y << " )" << endl;
    }
 
}
cs

 

문제를 풀기전 핵심은 복사생성자, 복사대입연산자는 깊은복사

이동생성자,이동대입연산자는 얕은 복사임을 기억하고 들어가자.

 

[최종코드]

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
#include <iostream>
 
using namespace std;
 
struct Point {
    int x, y;
};
 
class Polygon {
public:
    Polygon() {
        nPoints = 0;
        points = NULL;
    }
    Polygon(const int nPoints,const Point *points):nPoints(nPoints) {
        this->points = new Point[nPoints];
        for (int i = 0; i < nPoints; i++) {
            this->points[i] = points[i];
        }
    }
 
    //복사생성자 구현
 
    Polygon(const Polygon &rhs) {
 
        //객체가 생겨나는 시점이라 delete 없어도됨 
        nPoints = rhs.nPoints;
        points = new Point[nPoints];
        for (int i = 0; i < nPoints; i++) {
            points[i] = rhs.points[i];
        }
 
        cout << "깊은복사(생성자)" << endl;
 
    }
 
    //복사대입연산자 구현 
 
    Polygon& operator=(const Polygon& rhs) {
 
        if (this != &rhs) {
            delete[] points; //잘했으 
            nPoints = rhs.nPoints;
            points = new Point[nPoints];
 
            for (int i = 0; i < nPoints; i++) {
                points[i] = rhs.points[i];//깊은복사 
            }
        }
        cout << "깊은복사(연산자)" << endl;
        return *this;
        
    }
 
    ~Polygon() {
        delete[] points;
    }
 
    //이동 생성자구현
 
    Polygon(Polygon &&rhs){
        nPoints = rhs.nPoints;//얘는 깊은복사 문제 없음
        //포인터 얕은 복사 해야함.
        points = rhs.points;
        cout << "얕은복사(생성자)" << endl;
 
        //원본을 delete 해야함. 그래야 얘(rhs)가 사라질때 임시객체가 
        //가리키는 메모리가 남아있을 수 있음 
 
        rhs.points = NULL
    }
 
    //이동 대입 연산자 구현 
 
    Polygon& operator=(Polygon&& rhs) {
 
        if (this != &rhs) {
            nPoints = rhs.nPoints;
            delete[] points;
            points = rhs.points;//얕은복사 
            rhs.points = NULL;
        }
        cout << "얕은복사(연산자)" << endl;
        return *this;
    }
 
    int GetNPoints()const {
        return nPoints;
    }
 
    Point *GetPoints() const {
        if (nPoints == 0return NULL;
        return points;
    }
private:
    int nPoints;
    Point* points;
 
};
 
Polygon getSqaure() {
    Point points[4= { {0,0},{1,0},{1,1},{0,1} };
    Polygon p(4, points);
    return p;
}
 
int main() {
    Polygon a;
    a = getSqaure();//얕은 복사 2회 getSqaure r-value 함수가 반환될때 r-value임 
    Polygon b (a);//깊은 복사 1회 ->  이동생성자 아님 a가 r-value가 아니여서 
    Polygon c;
    c = a;//깊은 복사 1회 
 
    int nPoints = c.GetNPoints();
    Point* points = c.GetPoints();
    for (int i = 0; i < nPoints; i++) {
        cout << "( " << points[i].x << "," << points[i].y << " )" << endl;
    }
 
}
cs

 

 

 

여기서 궁금해지는게 있다.

보통 포인터를 깊은 복사할 때는 points[i]=*rhs.points[i] 또는 strcpy 이런걸 사용해주는데

 

복사생성자를 보면 등호만으로도 깊은복사가 될까? 라는 의문이다. 

이게 얕은 복사라 생각할 수 있는데 Polygon 구조체에 있는 x,y값은 포인터가 아니라 기본자료형인 int이다. 그래서 등호만으로도 깊은복사가 가능하다!

 

복사 생성자 vs 복사 대입연산자

나는 깊은복사 중에 생성자, 연산자 중 어떤게 호출됐는지 궁금해서 "깊은복사(생성자)" <-- 이런식으로 출력하게 해줬다. 그랬더니

 

깊은 복사를 했을 때 생성자 먼저 호출되고 -> 복사대입연산자가 호출된다. 왜 그런것일까?

 

우선 두 개념의 정의를 다시 살펴보자.

복사를 하면서 새 객체의 생성이 필요하면 복사생성자를 사용하고, 그렇지 않으면 대입연산자를 사용한다. 

복사 생성자는 객체가 새로 생성되는 시점에서 대입을 할 때 호출이 되고 대입 연산자는 객체 두 개가 이미 생성 및 초기화가 진행된 상태에서 대입을 할 때 호출이 된다.

 

 

main함수를 보면 객체 b는 객체가 생성되면서 복사가 같이 일어난다. 그러니까 새 객체의 생성이 일어나서 복사 생성자가 필요한거고

 

c객체는 먼저 객체가 생성되고 a객체를 대입해준다. 그니까 이미 객체가 있으니 이땐 복사대입연산자가 호출되는 것이다. 

 

참고자료

https://stdbc.tistory.com/115

 

생성자, 복사생성자, 복사대입연산자, 소멸자

출처 C++의 멤버함수 중 클래스 안에서 직접 선언하지 않으면 컴파일러가 자동으로 선언해주는 함수들 : 기본형 생성자 / 복사생성자 / 복사대입연산자 /소멸자 Class Empty{}; //위와 아래는 서로 같

stdbc.tistory.com

 

728x90
반응형

댓글