[ Effective C++ ] 정리 모음집
" C++ 프로그래머의 필독서, 스콧 마이어스의 Effective C++ 를 읽고 내용 요약 / 정리 "
" 다중 상속은 대단한 게 아닌, 그냥 객체 지향 기법으로 소프트웨어를 개발하는 데 쓰이는 도구중 하나! "
- 다중 상속은 단일 상속보다 확실히 복잡하다. 새로운 모호성 문제를 일으킬 뿐만 아니라 가상 상속이 필요해질 수도 있다.
- 가상 상속을 쓰면 크기 비용, 속도 비용이 늘어나며, 초기화 및 대입 연산의 복잡도가 커진다. 따라서 가상 기본 클래스에는 데이터를 두지 않는 것이 현실적으로 가장 실용적이다.
- 다중 상속을 적법하게 쓸 수 있는 경우가 있다. 인터페이스 클래스로부터 public 상속을 시킴과 동시에 구현을 돕는 클래스로부터 private 상속을 시키는 것
- 단일 상속이 좋다면 다중 상속은 더 좋을 것
class BorrowableItem
{
public:
void checkOut();
...
};
class ElectronicGadget
{
private:
bool CheckOut() const;
...
};
class MP3Player :
public BorrowableItem,
public ElectronicGadget
{
...
};
MP3Player mp;
mp.checkOut(); // 누구의 checkOut 인가?? 모호성 발생
두 checkOut 함수들 중에서 파생 클래스가 접근할 수 있는 함수가 딱 결정 되는 것이 분명한데도 모호성이 생긴다
- BorrowableItem 에서는 public 멤버, ElectronicGadget 에서는 private 멤버
- 중복된 함수 호출 중 하나를 골라내는 C++의 규칙을 따른 결과이다
- 함수의 접근 가능성보다 최적으로 일치하는 함수인지를 먼저 확인한다
- 위 코드의 경우 checkOut 함수는 C++의 규칙에 의한 일치도가 서로 같기 때문에 함수가 결정되지 않는다
손수 함수를 지정해 주어 해결하자!
mp.BorrowableItem::checkOut();
class File { ... };
class InputFile : public File { ... };
class OutputFile : public File { ... };
class IOFile : public InputFile,
public OutputFile
{ ... };
📢 만약 File 클래스에 fileName 데이터 멤버가 있다면 IOFile에는 몇개의 fileName 데이터 멤버가 있어야 할까
- 바로 위의 기본 클래스로부터 사본을 하나씩 물려받게 되니까 fimeName은 두 개 이어야 한다.
- 두 기본 클래스로부터 fileName을 동시에 물려 받아도 fileName이 중복되면 안 될 것 같다.
C++은 위 두 가지를 모두 지원한다!
1번의 경우를 기본적으로 지원한다.
2번의 경우라면 가상 상속을 이용한다.
class File { ... };
class InputFile : virtual public File { ... };
class OutputFile : virtual public File { ... };
class IOFile : public InputFile,
public OutputFile
{ ... };
- 하지만 가상 상속의 비용은 비싸다. (크기, 속도 비용 뿐만 아니라 초기화 및 대입에서도 비용이 더 나간다)
- 따라서 구태여 쓸 필요가 없다면 가상 상속을 사용하지 말아야 한다. 그럼에도 가상 상속을 사용해야 한다면 가상 기본 클래스에는 데이터를 넣지 않는 쪽으로 생각하자
class IPerson
{
public:
virtual ~IPerson();
virtual string name() const = 0;
virtual string birthDate() const = 0;
};
class DatabaseID { ... };
class PersonInfo
{
public:
explicit PersonInfo(DatabaseID pid);
virtual ~PersonInfo();
virtual const char* theName() const;
virtual const char* theBirthDate() const;
virtual const char* valueDelimOpen() const;
virtual const char* valueDelimClose() const;
...
};
class CPerson : public IPerson, private PersonInfo
{
public:
explicit CPerson(DatabaseID pid) : PersonInfo(pid) {}
virtual string name() const
{ return PersonInfo::theName(); }
virtual string birthDate() const
{ return PersonInfo::theBirthDate(); }
private:
const char* valueDelimOpen() const { return ""; }
const char* valueDelimClose() const { return ""; }
};