[Effective C++] # 생성자, 소멸자, 복사 생성자, 복사 대입 연산자
Scott Meyers의 "Effective C++"를 통해, C++ 구현에 필요한 개념들을 이해하고, 기록하기 위함입니다. 해당 항목은 2장 "생성자, 소멸자 및 대입 연산자", 항목 5 "C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자"에 해당하는 내용입니다.
생성자, 소멸자, 복사 생성자, 복사 대입 연산자
class Empty {};
위 예제 코드는 비어 있는 클래스를 정의했습니다. 하지만, C++는 비어 있는 클래스 안에 몇 개의 멤버 함수를 자동으로 생성합니다. 이 멤버 함수들은 "생성자", "소멸자", "복사 생성자", 그리고 "복사 대입 연산자"입니다. 이들은 모두 public 멤버이며, inline 함수입니다. 이처럼 비어 있는 클래스는 아래 코드와 같다고 볼 수 있겠습니다.
class Empty {
public:
Empty() {...}; // 생성자
Empty(const Empty& rhs) {...}; // 복사 생성자
~Empty(){...}; // 소멸자
Empty& operator=(const Empty& rhs) {...}; // 복사 대입 연산자
};
Empty e1;
Empty e2 (e1); // 복사 생성자
e2 = e1; // 복사 대입 연산자
보시다시피, 비어 있는 클래스는 실제로 비어있지 않고, 컴파일러가 자동으로 생성하는 생성자, 소멸자, 복사 생성자, 그리고 복사 대입 연산자를 갖고 있다고 생각해도 되겠습니다. 잘 알고있는 생성자와 소멸자는 내버려 두고, 우리는 복사 대입 연산자와 복사 생성자에 더 집중해보겠습니다.
복사 생성자, 복사 대입 연산자
tmeplate<Typename T>
class Name{
public:
Name(const char *name, const T& value);
Name(const std::string& name, const T& value);
...
private:
std:string nameValue;
T objectValue;
};
Name<int> no1("Hello", 2); // 생성자
Name<int> no2 (no1); // 복사 생성자
위 예제 코드를 살펴보겠습니다. 먼저, "Name" 클래스는 직접 정의한 "생성자"를 갖고 있습니다. 따라서, 컴파일러가 자동적으로 기본 생성자를 생성하지 않게 되겠죠. 그리고, "no2" 객체는 복사 생성자를 사용하여 "no1"로부터 값을 전달받습니다. no1.nameValue와 no1.objecValue 모두 정상적으로 no2.nameValue와 no2.objectValue로 값을 전달합니다. 하지만, 이러한 동작 과정은 소스코드가 적법해야 정상적으로 이루어집니다. 무슨 얘기인지 자세하게 알아보겠습니다.
복사 생성자, 복사 대입 연산자 오류 발생 예제
tmeplate<Typename T>
class Name{
public:
Name(const char *name, const T& value);
Name(const std::string& name, const T& value);
...
private:
// std::string nameValue -> std::string& nameValue 로 바꾸고
std:string& nameValue;
// T objectValue -> const T objectValue로 바꾸었다
const T objectValue;
};
Name<int> p ("Hello", 23);
Name<int> p2 ("Bye", 43);
p = p2; //???????????????????????
위 예제 코드를 살펴보겠습니다. 기존의 "Name" 클래스와 같지만, 데이터 멤버들의 형식을 바꾸었습니다. nameValue는 "std::string"에서 "std::string&"로 바꾸었고, ojbectValue도 일반 정수 타입에서 상수로 바꾸었습니다. 같은 "Name" 클래스 객체 "p"와 "p2" 사이에 복사 대입 연산자를 수행하면 어떻게 될까요? 에러가 발생합니다. C++의 참조자는 현재 자신이 참조하는 것과 다른 객체를 참조할 수 없기 때문입니다. 따라서, 참조자를 데이터 멤버로 갖고 있는 경우, 우리는 컴파일러가 자동으로 생성해주는 복사 대입 연산자를 사용하면 안 되겠죠. 직접 정의해야 합니다.
컴파일러는 경우에 따라서 클래스에 대해 기본 생성자, 소멸자, 복사 생성자, 그리고 복사 대입 연산자를 암시적으로 만들어 놓을 수 있습니다.
'언어 > Effective C++' 카테고리의 다른 글
[Effective C++] #7 기본 클래스와 가상 소멸자 (0) | 2022.01.07 |
---|---|
[Effective C++] #6 사용자 정의 기본 멤버 함수 (0) | 2022.01.07 |
[Effective C++] #4 객체의 초기화 (0) | 2022.01.06 |
[Effective C++] #3_const 사용 (0) | 2022.01.05 |
[Effective C++] #2_#define, 매크로 사용의 대안 (0) | 2022.01.03 |