Chapter 2. 생성자, 소멸자 및 대입 연산자 part1 항목 5 - 9

Effecive C++ 정리

Posted by Kyung Jun Cha on 2019-10-24

Chapter 2. 생성자, 소멸자 및 대입 연사자


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


  • 경우에 따라 클레스에 대해 기본 생성자, 복사 생성자, 복사 대입 연산자, 소멸자 를 암시적으로 만들어 놓을 수 있다.
  • 이들은 모두 public 멤버이며 inline 함수입니다.

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


  1. 컴파일러에서 자동으로 제공하는 기능을 허용치 않으려면, 대응되는 멤버 함수를 private로 선언한 후에 구현은 하지 않은 채로 둔다.
1
2
3
4
5
6
7
8
class HomeForSale {
public:
...
private:
...
HomeForSale(const HomeForSale&); //선언만 달랑 있습니다.
//매개 변수는 사용될 일이 없기에 선언하지 않아도 됩니다.
};
  1. 대응되는 함수를 private으로 선언하되, 이것을 별도의 기본 클래스에 넣고 이것으로부터 파생시킨다.

링크 에러전에 컴파일러 에러로 발견할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
class Uncopyable {
protected:
Uncopyable() {}
~Uncopyable() {}
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};

class HomeForSale : private Uncopyable{
...
};

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


  • 기본 클래스 포인터 를 통해 파생 클래스 객채가 삭제될 떄 그 기본 클래스에 비가상 소멸자가 들어 있으면 프로그램 동작은 미정의 사항이다.
  • 따라서 가상 함수를 하나라도 가진 클래스는 가상 소멸자를 가져야 하는게 대부분 맞다.
  • 기본 클래스로 설계되지 않았거나 다형성을 갖도록 설계되지 않은 클래스에는 가상 소멸자를 선언하지 말아야 합니다.
  • 순수 가상 소멸자 활용 : 추상 클래스였으면 좋겠는데 마땅히 넣을 만한 순수 가상 함수가 없을 때 사용
1
2
3
4
5
6
7
8
class AMOV {
public:
virtual ~AMOV() = 0; // 순수 가상 소멸자 선언
};

AMOV::~AMOV() {} // 순수 가상 소멸자 정의
// 가상 함수를 통해 파생 클래스의 소멸자를 호출하더라도
//기본 클래스의 소멸자도 마지막으로 호출되기에 없으면 링크 에러가 발생합니다.

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


  1. 소멸자에서는 예외가 빠져나가면 안 됩니다.
  2. 소멸자 안에서 호출된 함수가 예외를 던질 가능성이 있다면, 어떤 예외이던지 소멸자에서 모두 받아낸 후에 삼켜 버리든지 프로그램을 끝내든지 해야 합니다.
  • 예외 발생시 프로그램을 끝내는 경우
1
2
3
4
5
6
7
8
9
DBConn::~DBConn()
{
try {db.close();}
catch(...)
{
//close 호출 실패 로그 작성
std::abort();
}
}
  • 예외 발생시 예외를 삼키는 경우

발생한 예외를 그냥 무시한 뒤라도 프로그램이 신뢰성 있게 실행을 지속할 수 있어야 한다.

1
2
3
4
5
6
7
8
DBConn::~DBConn()
{
try {db.close();}
catch(...)
{
//close 호출 실패 로그 작성
}
}
  1. 예외에 대해 사용자가 반응해야 할 필요가 있다면 해당 연산을 제공하는 함수는 반드시 보통의 함수(소멸자가 아닌 함수)이어야 합니다.

예외는 소멸자가 아닌 다른 함수에서 비롯된 것이어야 한다. > 사용자에게 에러를 처리할 수 잇는 기회를 주는 것입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class DBConn {
public:
...
void close() //소멸자가 아닌 보통의 함수를 통해 예외 가능성 있는 연산 실행
{
db.close();
closed = true;
}
~DBConn()
{
if(!closed) //사용자가 연결을 안 닫았으면 닫습니다.
try { db.close(); }
catch(...)
{
//close 호출 실패 로그 작성
...
}
}
private:
DBConnection db;
bool closed;
};

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


  • 생성자 혹은 소멸자 안에서 가상 함수를 호출하지 마세요. 가상 함수라고 해도 지금 실행중인 생성자나 소멸자에 해당되는 클래스의 파생 클래스 쪽으로는 내려가지 않으니까요.
  • 순수 가상 함수의 경우 에러가 발생합니다.