[C++] 항목 5: C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자
카테고리: cpp
태그: cpp
이 글은 아래의 책을 정리하였습니다.
이펙티브 C++ 제3판, 스콧 마이어스 저자, 곽용재 번역
📦 2. 생성자, 소멸자 및 대입 연산자
👉🏻 항목 5: C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자
1. C++이 생성해주는 멤버 함수
- 기본 생성자
- 소멸자
- 복사 생성자
- 복사 대입 연산자
이 4가지의 멤버 함수는 개발자가 선언해주지 않으면, 컴파일러가 대신 생성해준다. 이들은 public 멤버이며, inline 함수로 생성된다.
1.1. 선언/호출
class Empty {
public:
Empty() { ... } // 기본 생성자
Empty(const Empty& rhs) { ... } // 복사 생성자
~Empty() { ... } // 소멸자
Empty& operator=(const Empty& rhs) { ... } // 복사 대입 연산자
}
Empty e1; // 기본 생성자/소멸자 호출
Empty e2(e1); // 복사 생성자 호출
e2 = e1 // 복사 대입 연산자 호출
이때 소멸자는 이 클래스가 상속한 기본 클래스의 소멸자가
가상(virtual) 소멸자로 되어있지 않으면, 비가상 소멸자로 만들어진다.
1.2. 복사 생성자/복사 대입 연산자
template <tyename T>
class NamedObject {
public:
// 생성자
NamedObject(const char* name, const T& value);
NamedObject(const string& name, const T& value);
private:
string nameValue;
T objectValue;
}
NamedObject<int> no1("Smallest Prime Number", 2); // 생성자 호출
NamedObject<int> no2(no1); // 복사 생성자 호출
NamdObject 클래스 내에는 생성자가 선언되어 있다면, 컴파일러는 기본 생성자를 생성하지 않는다.
이 코드에선 복사 연산자나 복사 대입 연산자가 보이지 않는다.
필요한 경우 복사 연산자/복사 대입 연산자를 호출하게 된다.
생성된 복사 생성자는 no1.nameValue와 no1.objectValue를 이용하여 no2.nameValue와 no2.objectValue를 각각 초기화해준다.
nameValue는 string 타입이므로 string 자체적으로 포함되어있는 복사 생성자를 통해 초기화 해준다.
nameObject는 int이므로 기본 제공 타입이다. 이런 경우 no1.objectValue의 각 비트를 그대로 복사해 온다.
1.2. 암시적으로 생성할 수 없는 경우
template <class T>
class NamedObject {
public:
NamedObject(string& name, const T& value);
...
private:
string& nameValue; // 참조자로 변경
const T objectValue; // 상수로 변경
}
string newDog("Persephone");
string oldDog("Satch");
NamedObject<int> p(newDog, 2);
NamedObject<int> s(oldDog, 36);
p = s; // 복사 대입 연산자
만약 클래스 내의 변수가 참조자와 상수가 있다면 어떻게 될까?
이러한 경우 C++에서 컴파일을 거부하게 되며, 이유는 아래와 같다.
1. nameValue : C++의 참조자는 원래 자신이 참조하고 있는 것과 다른 객체를 참조할 수 없다.
2. objectValue : 이미 초기화된 상수의 값을 변경할 수 없다.
3. 앞선 이유로, 복사 대입 연산자를 생성할 수 없다.
에러를 해결하기 위해선, 복사 대입 연산자를 직접 만들어주어야 할것이다.
이외에도, 생성한 클래스가 복사 대입 연산자를 private로 선언한 기본 클래스로부터 파생된 경우에도, 암시적 복사 대입 연산자를 가질수 없다.
🧐 정리
- 컴파일러는 경우에 따라 클래스에 대해 기본 생성자, 소멸자, 복사 생성자, 복사대입 연산자를 암시적으로 생성한다.
- 암시적으로 생성할 수 없는 경우 생성하지 않으며, 필요시 직접 생성해주어야 한다.
- 생성자가 이미 존재한다면, 컴파일러는 기본 생성자를 생성하지 않는다.
댓글남기기