[ Effective C++ ] 항목 40 : 다중 상속은 심사숙고해서 사용하자

Minsu._.Lighting·2023년 12월 11일
0

[ 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();

📌 다중 상속시 "죽음의 MI 마름모꼴(deadly MI diamond)" 발생

class File { ... };

class InputFile : public File { ... };

class OutputFile : public File { ... };

class IOFile : public InputFile,
			   public OutputFile
{ ... };

📢 만약 File 클래스에 fileName 데이터 멤버가 있다면 IOFile에는 몇개의 fileName 데이터 멤버가 있어야 할까

    1. 바로 위의 기본 클래스로부터 사본을 하나씩 물려받게 되니까 fimeName은 두 개 이어야 한다.
    1. 두 기본 클래스로부터 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 ""; }
};
  • 인터페이스의 public 상속과 구현의 private 상속을 조합
profile
오코완~😤😤

0개의 댓글

관련 채용 정보