복사 생성자, 소멸자 정리

2023. 3. 20. 21:03개인공부/C++

new와 malloc의 차이점 

new와 malloc 모두 동적으로 할당하지만 '무언가' 다르다고 했었는데, 바로 new의 경우 객체를 동적으로 생성하면서와 동시에 자동으로 생성자도 호출핸다는 점이다.

소멸자(Destructor)

만약에 name 을 동적으로 생성해서 문자열을 복사하였는데, 이렇게 동적으로 할당된 char 배열에 대한 delete는 언제 이루어지는 것인가요? 명확하게 delete를 지정하지 않은 한 자동으로 delete 가 되는 경우는 없다. 다시 말해서 동적으로 할당했던 저 name 은 영원히 메모리 공간 속에서 둥둥 떠다닌다는 말이다. 위와 같은 name들이 쌓이고 쌓이게 되면 메모리 누수(Memory Leak)이라는 문제점이 발생하게 된다. 이러한 문제점을 해결하기 위해서 소멸자를 사용해야 한다! 소멸자는 인자를 아무것도 가지지 않는다 그러므로 소멸자는 오버로딩도 되지 않는다. 참고로 우리가 따로 생성자를 정의하지 않더라도 디폴트 생성자가 있었던 것처럼, 소멸자도 디폴트 소멸자(Default Destructor)가 있다. 물론, 디폴트 소멸자 내부에선 아무런 작업도 수행하지 않는다. 만일 소멸자가 필요 없는 클래스라면 굳이 소멸자를 따로 써줄 필요는 없다.

복사 생성자 (Copy constructor)

복사 생성자는 생성자(const 클래스이름& other); 로 정의가 가능하다.

dog2 는 복사 생성자가 1번 호출되는 것이고, 아래 것은 그냥 생성자가 1번 호출된 것이다. 복사 생성자는 오직 "생성"시에 호출된다는 것을 명심해야 한다. 그런데 사실 디폴트 생성자와 소멸자처럼, C++ 컴파일러는 이미 디폴트 복사 생성자(Default copy constructor)를 지원해 주고 있다. 

디폴트 복사 생성자의 한계

위의 코드를 실행해보면 알 수 있듯이 만약에 name과 같이 복사생성자가 포인터를 가지고 있다면 name 해제하고  해제된 메모리에 다시 접근해서 delete를 하는 문제가 발생한다. 그러므로 복사 생성자에서 name을 그대로 복사하지 말고 따로 다른 메모리에 동적할당을 해서 그 내용만 복사하면 된다. 이렇게 새로 할댕해서 내용을 복사하는 것을 깊은 복사(deep copy) 라고 부르며 아까처럼 단순히 대입만 해주는 것을 얕은 복사(shallow copy)라고 부릅니다. 컴파일러가 생성하는 디폴트 생성자의 경우 얕은 복사 밖에 할 수 없으므로 위와 같이 깊은 복사가 필요한 경우에는 직접 복사 생성자를 만들어야 한다.

생각해 보기

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>
class string {
    char* str;
    int len;
 
public:
    string(char c, int n);  // 문자 c 가 n 개 있는 문자열로 정의
    string(const char* s);
    string(const string& s);
    ~string();
 
    void add_string(const string& s);   // str 뒤에 s 를 붙인다.
    void copy_string(const string& s);  // str 에 s 를 복사한다.
    int strlen();                       // 문자열 길이 리턴
};
 
string::string(char c, int n)
{
    len = n;
    str = new char[n+1];
    for (int i = 0; i < n; ++i)
        str[i] = c;
    str[n] = '\0';
}
 
string::string(const char* s)
{
    len = 0;
    while (s[len])
        ++len;
    str = new char[len+1];
    strcpy_s(str, len + 1, s);
}
 
string::string(const string& s)
{
    len = s.len;
    str = new char[sizeof(s.str)];
    strcpy_s(str, sizeof(s.str), s.str);
}
 
string::~string()
{
    delete str;
}
 
void string::add_string(const string& s)
{
    char* tmp = new char[sizeof(str) + sizeof(s.str)-1];
    for (int i = 0; i < len; ++i)
    {
        tmp[i] = str[i];
    }
    for (int i = len; i < len + s.len; ++i)
    {
        tmp[i] = s.str[i - len];
    }
    tmp[len + s.len] = '\0';
    delete str;
    str = tmp;
    len += s.len;
}
 
void string::copy_string(const string& s)
{
    str = new char[sizeof(s.str)];
    strcpy_s(str, sizeof(s.str), s.str);
    len = s.len;
}
 
int string::strlen()
{
    return len;
}
cs

'개인공부 > C++' 카테고리의 다른 글

sizeof  (0) 2023.03.30
const, static 정리  (0) 2023.03.21
함수의 오버로딩, 생성자 정리  (0) 2023.03.19
객체 지향 프로그래밍 정리  (0) 2023.03.18
참조자 공부 내용 정리  (1) 2023.03.17