Effective C++ 항목 5 : c++ 가 은근슬쩍 만드는 함수에 촉각을 세우자.
C++ 컴파일러는 컴파일 과정에서 클래스 내 필요한 멤버함수가 없으면 암시적으로 넣어준다. 바로 생성자, 소멸자, 복사 생성자, 복사 대입 연산자등이 컴파일러가 암시적으로 만들어주는 것들이다.
class Empty();
라고 썼다면, 근본적으로 아래와 같이 컴파일러가 넣어주는데..
class Empty()
{
public:
Empty() { ... } // 기본 생성자
Empty(const Empty& rhs) { ... } // 복사 생성자
~Empty() { ... } // 소멸자
Empty& operator=(const Empty& rhs) { ... } // 복사 대입 연산자
}
위와 같이 암시적으로 몇 가지 멤버함수를 넣어준다. 이들은 모든 클래스에 다 넣어주는 것이아니라 필요할 때 넣어주는데, 보통 조건이 몇가지 있다.
기본생성자, 소멸자 생성 - Empty e1;
복사 생성자 - Empty e2(e1);
복사 대입 연산자 - e2 = e2;
위와 같이, class Empty를 사용함에 있어 클래스 내 멤버 함수가 존재하지 않는다면 해당 조건에 맞게 멤버함수들을 암시적으로 생성해준다.
복사 대입 연산자 주의점
복사 대입 연산자 같은 경우 적법과 합리적을 따져 조건에 부합하지 않으면 컴파일러는 기본적으로 생성하지 않는다.
그렇다면, 어떤 기준으로 조건의 적법유무를 판단하는 것일까?
template<typename T>
class NamedObject {
public:
NamedObject(const std::string& name, const T& value);
...
private:
std::string& nameValue;
const T objectValue;
};
int main() {
std::string newDog("nd");
std::string oldDog("od");
NamedObject<int> p (nd, 2);
NamedObject<int> s (od, 22);
p = s;
}
위의 코드가 가능할까? 위의코드는 불가능하다. 생각해보면, 멤버변수가 string& 형으로 가지고 있다. 두 객체는 각각 다른 string 참조형을 가지고 있다. 이럴 경우 대입 연산은 불가능해진다. C++의 참조자는 원래 자신이 참조하고 있는 것과 다른 객체를 참조할 수 없기 때문이다. 즉, 내가 지정한 참조자 외에 다른 걸 참조할 수가 없다.
해당 복사 대입 연산자를 구현하기 위해선 깊은복사를 하든 추가로 변경이 필요하다. 즉, 사용자가 정의해야한다. 이런 애매모호한 상황에서는 컴파일러는 암시적 생성 거부를 선언한다. (적법하지 않다고 판단한 것이다.)
또 const를 보자. const의 경우에도 상수성인 값을 복사하는 행위자체가 적법하지 않다고 판단해 여기서도 컴파일러가 암시적 생성 거부를 선언한다.
애매한 경우 컴파일러가 암시적 생성을 거부하도록 하는 것이다.