C++ 클래스를 사용 하다보면, 컴파일러가 은근슬쩍 만들어내는 함수가 4가지가 있다.
이것들이 보통의 경우 별로 신경 쓸 일이 없는데, 다음과 같은 상황에선 손을 좀 써 줘야 한다.
1. 기본 생성자 요구 (default)
class NonCopyable
{
private:
NonCopyable(const NonCopyable&);
};
int main()
{
// Compile error : C2512: 'NonCopyable' : 사용할 수 있는 적절한 기본 생성자가 없습니다.
NonCopyable nc;
}
기본 생성자는 기본 생성자가 아닌, 어떠한 생성자라도 존재할 경우 자동으로 생성되지 않는다.
(이것이 복사 생성자가 되었던, 인자를 가지는 다른 형태의 생성자이든 말이다).
따라서, 기본 생성자 형으로 객체를 생성하면, 사용할 수 있는 적절한 기본 생성자가 없다며 컴파일 에러가 발생한다.
이 경우, 아래와 같이 기본 생성자를 직접 선언/정의해 주어야 컴파일 에러가 발생하지 않는다.
class NonCopyable
{
public:
NonCopyable() {}
private:
NonCopyable(const NonCopyable&);
};
int main()
{
NonCopyable nc;
}
이 경우 C++11에서는 아래와 같이 default 생성자를 선언만으로 해결할 수 있다.
class NonCopyable
{
public:
NonCopyable() = default;
private:
NonCopyable(const NonCopyable&);
};
int main()
{
NonCopyable nc;
}
위 예제에서는 NonCopyable() 생성자의 default 생성을 명시적으로 요구하였으므로, 기본 생성자가 생성된다.
2. 복사, 대입 금지 클래스 (delete)
특정 클래스에 대해선 복사도 대입도 막고 싶은 경우 기존 C++ 0x 까진 아래와 같이 작성하였다.
class NonCopyable
{
public:
NonCopyable() {}
~NonCopyable() {}
private:
NonCopyable(const NonCopyable&);
NonCopyable& operator = (const NonCopyable&);
};
위 방식은 우선 복사 생성자와 대입 연산자를 private으로 두어 외부에서 접근 에러가 발생하게 하는 방식이고,
만약, 위 코드에서 복사 생성자와 대입 연산자의 접근 지정자를 public으로 두었다면,
선언만 하고 정의를 하지 않아 링크 에러가 나게 하는 식으로 사용을 금지시키는 방식이었다.
이를 C++11의 delete를 명시적으로 붙여주면, 이들에 대한 사용이 확실히 금지 된다.
class NonCopyable
{
public:
NonCopyable() {}
~NonCopyable() {}
// 명시적으로 복사 생성자 / 대입 연산자가 disable 처리 되었음
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator = (const NonCopyable&) = delete;
};
음 여기까지만 봤을 땐, 뭐 기존에 비해 크게 이득이 뭔가 싶겠지만,
delete의 경우 조금 더 요긴하게 사용할 수 있다.
delete 키워드는 아무 함수에나 사용할 수 있기 때문에,
멤버 함수의 파라미터에 대해 암시적인 형변환이 일어나는 것을 사전에 방지할 수 있다.
// int 타입에 대해선 받지 않겠음.
struct NoInt
{
void f(double i);
void f(int) = delete;
};
// f의 인자로 double 형을 제외하고는 완전히 금지하겠다.
// 즉, 컴파일러가 암시적으로 형 변환하는 것까지 완벽 차단.
struct OnlyDouble
{
void f(double d);
template<class T> void f(T) = delete;
};
지금까지 default / delete 키워드에 대해 알아 보았지만, 아쉽게도 Visual Studio2013부터만 지원된다.