개발자의 길/Effective C++

[Effective C++] Chapter 2 생성자, 소멸자 및 대입 연산자

토아드 2019. 3. 24. 18:57
반응형

항목 5 : C++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자.

이유 : 컴파일러는 클래스의 생성자, 소멸자 등을 자동으로 만들어 내는데, 만약 멤버 변수로 포인터 등이 존재할 경우. 완전한 깊은 복사를 수행하지 않게 된다. 

해결 : 클래스 선언시 생성자, 복사 생성자, 복사 대입 생성자, 소멸자, 이동 생성자( c++ 11) 이동 복사 생성자 (c++ 11) 등을 명시적으로 선언해두자.


항목 6 : 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해 버리자

이유 : 구조적으로 복사되면 안되는 경우 ( 단 하나만 존재하는 클래스 등 ) 클래스를 사용하는 쪽에서 자동으로 생성된 복사 생성자를 이용해 버릴 수 있다.

해결 : 자동으로 생성되는 함수들을 private 로 선언하거나, = delete 키워드를 선언해 줌으로써 명시적으로 사용을 금지시키자.

항목 7 : 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자

이유 : 파생 클래스를 기본 클래스 타입의 변수에 대입하고 소멸을 시킬 때, 기본 클래스의 소멸자를 호출하기 때문이다.

해결 : 소멸자를 가상 소멸자로 선언함으로써, 다형성이 적용되게 하면 해결된다.


항목 8 : 예외가 소멸자를 떠나지 못하도록 붙들어 놓자

이유 : 에외가 소멸자를 떠난다는 의미는, 소멸자에서 예외상황이 발생하고 소멸자를 빠져나가지 않도록 하자는 것이다. STL컨테이너에 10 개의 클래스 인스턴스를 보관하다가 벡터를 할당 해제하며 2번째 클래스 인스턴스 소멸자에서 에러가 난 경우, 또 3번째에서 또 난 경우 예외가 2번이나 발생하게 되고, c++에서는 정의되지  않은 동작을 야기한다고 한다.

해결 : 소멸자 내에서 예외를 삼켜 버리거나 (무시하거나), 프로그램을 바로 끝내도록 하자. 또는 예외가 일어날 가능성이 있는 코드를 사용자가 호출하도록 함수로 빼 버리자.


항목 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자

이유 : 기본 클래스와 파생 클래스가 존재하고, 파생 클래스를 생성하면 먼저 기본 클래스의 생성자가 호출되고, 그 다음 파생 클래스의 생성자가 호출된다. 이 과정에서 기본 클래스 생성자 내부의 가상 함수는 기본 클래스의 가상 함수를 호출하게 된다. 그렇기 때문에 프로그래머의 예상과 다른 동작을 할 가능성이 있다.
* 중요한 점 : 파생 클래스가 생성 되면서 기본 클래스의 생성자를 호출하는 동안에는 그 객체는 '기본 클래스' 이다.

해결 : 굳이 생성자에서 호출해야 한다면. 비 가상 함수로 선언하고, 파생 클래스에서 생성자를 호출 할 때 기본 클래스의 생성자를 호출하게 만들자. 

예 :

BaseClass {
 BaseClass ( baseParameters )
 {
    doSomething( baseParameters )
 }
};

DerivedClass {
public:
  DerivedClass ( parameters )
   : BaseClass ( makeForBase( parameters ) )
  {
  }
};
 위 상황은 '파생 클래스' 에서 '기본 클래스' 로 정보를 넘겨 주고 싶을 경우에 저렇게 하라는 소리이다. 


항목 10 : 대입 연산자는 *this의 참조자를 반환하게 하자

cout << "1" << "2" << "3" << endl;

value1 = value2 = value3 = 4;

위와 같이 c++ 에서는 대입 연산자를 사슬처럼 엮어서 사용 할 수 있도록 설계되어 있다.

이런 관례를 지키는 편이 좋기 때문에 대입 연산자의 리턴값을 *this 를 리턴하라고 한다.


항목 11 : operator= 에서는 자기대입에 대한 처리가 빠지지 않도록 하자

이유 : 서로 다른 객체를 받아서 처리하는 함수가 있을 때, 같은 객체가 들어오는 경우가 있다. A 가 B 라는 인스턴스를 받아서 A의 원소를 제거하고 B를 대입하는 경우에 A == B 관계가 성립할 경우, B의 원소를 제거하고 B를 제거하는 이상한 동작을 하게 된다. 이런 경우를 피하기 위해 예외처리를 항상 해 두자

해결 : 언제나 자기 자신이 파라미터로 들어올 경우에 대한 대비를 하자. ( 비교를 하거나, 대입 후 제거를 하거나)


항목 12 : 객체의 모든 부분을 빠짐없이 복사하자

이유 : 프로그래머가 객체를 복사하는 함수를 생성할 때, 실수로 기본 클래스의 값 등을 복사하지 않고 넘어가는 경우가 생길 수 있다

해결 : 복사 생성자를 호출한다면 기본 클래스의 복사 생성자도 같이 호출을 하고, 해당 멤버의 모든 값을 복사하도록 구현하자





잘못 이해한 부분에 대한 태클, 지적 환영합니다.



반응형