[Effective C++ 정리 #5] 컴파일러가 몰래 생성하는 특별 함수들
이 글은 『Effective C++』를 읽고 개인적으로 공부한 내용을 정리한 기록입니다.
저는 컴퓨터공학을 전공하지 않았으며, 프로그래밍을 공부하는 과정에서의 이해와 생각을 정리하기 위해 글을 작성하고 있습니다.
따라서 내용 중 일부에 오류나 부정확한 설명이 있을 수 있으며, 피드백은 언제든지 환영합니다. 확인 후 수정하도록 하겠습니다.
전문적인 해설이 아닌 개인적 시선에서의 정리임을 참고하고 읽어주시면 감사하겠습니다.
[Effective C++ 정리 #5] 컴파일러가 자동으로 생성하는 네 가지 함수
C++에서는 우리가 명시적으로 작성하지 않아도, 컴파일러가 자동으로 생성해주는 함수들이 있습니다. 이러한 함수들은 클래스의 생명주기를 책임지는 중요한 역할을 하며, 우리가 직접 작성한 코드보다 먼저 동작하기도 합니다.
이번 Item에서는 컴파일러가 어떤 조건에서 어떤 함수를 만들어주는지, 그리고 그 함수들이 어떻게 동작하는지 구체적으로 살펴봅니다.
컴파일러가 자동 함수
C++ 컴파일러는 클래스에 다음 4가지 특별 함수들을 자동으로 생성할 수 있습니다.
- 기본 생성자 (default constructor)
- 복사 생성자 (copy constructor)
- 복사 대입 연산자 (copy assignment operator)
- 소멸자 (destructor)
이 함수들은 우리가 명시적으로 작성하지 않아도, 컴파일러가 필요하다고 판단되면 자동으로 생성하며, 자동 생성된 함수들은 public이고 inline 함수로 처리됩니다.
예를 들어, 아무것도 없는 클래스가 있다고 해봅시다:
class Empty {};
사실상 이 코드는 다음과 같은 코드와 동일하게 간주됩니다.
class Empty {
public:
Empty(); // 기본 생성자
Empty(const Empty& rhs); // 복사 생성자
~Empty(); // 소멸자
Empty& operator=(const Empty& rhs); // 복사 대입 연산자
};
언제 자동으로 생성될까?
컴파일러는 "정말 필요한 경우"에만 이 함수들을 생성합니다. 간단한 예시를 보겠습니다:
Empty e1; // 기본 생성자와 소멸자 필요
Empty e2(e1); // 복사 생성자 필요
e2 = e1; // 복사 대입 연산자 필요
이처럼 사용되는 문맥에 따라 자동으로 해당 함수가 생성됩니다.
기본 생성자와 소멸자는 보통 멤버나 베이스 클래스의 생성자·소멸자를 호출하기 위한 용도로 활용됩니다. 단, 생성된 소멸자는 **virtual이 아닙니다** (단, 상속받는 베이스 클래스가 virtual 소멸자를 가질 경우 예외).
자동 생성 함수는 어떤 동작을 할까?
복사 생성자와 복사 대입 연산자의 경우, **각 멤버를 단순히 복사**합니다. 예를 들어, 다음과 같은 템플릿 클래스가 있다고 해봅시다.
template<typename T>
class NamedObject {
public:
NamedObject(const std::string& name, const T& value);
private:
std::string nameValue;
T objectValue;
};
위 클래스는 복사 생성자와 복사 대입 연산자를 명시적으로 정의하지 않았기 때문에, 컴파일러가 자동으로 생성합니다.
NamedObject<int> no1("Smallest Prime Number", 2);
NamedObject<int> no2(no1); // 복사 생성자 호출
이 경우 no2.nameValue
는 string 타입이므로 string의 복사 생성자를 호출하게 되고, no2.objectValue
는 int 타입이므로 그냥 비트 복사합니다.
복사 대입 연산자도 마찬가지로 각 멤버를 순서대로 복사하게 됩니다.
자동 생성이 거부되는 경우
하지만 모든 경우에 자동 생성이 되는 건 아닙니다. 다음은 자동 생성이 불가능한 예입니다:
template<typename T>
class NamedObject {
public:
NamedObject(std::string& name, const T& value);
private:
std::string& nameValue; // 참조형 멤버
const T objectValue; // const 멤버
};
참조형 멤버 nameValue
는 한 번 초기화되면 다른 것을 참조할 수 없고, const 멤버 objectValue
는 값을 바꿀 수 없기 때문에, 복사 대입 연산자에서 복사가 불가능합니다.
즉, 컴파일러는 이 경우 "어떻게 복사해야 할지 모르겠다"며 자동 생성을 거부합니다. 이럴 때는 반드시 개발자가 명시적으로 복사 대입 연산자나 복사 생성자를 작성해야 합니다.
핵심 요약
- 컴파일러는 기본 생성자, 복사 생성자, 복사 대입 연산자, 소멸자를 암시적으로 생성할 수 있습니다.
감사합니다.