티스토리 뷰
6. 상속, 그리고 객체 지향 설계 (1)
항목 32 : public 상속 모형은 반드시 "is-a(...는 ...의 일종이다)"를 따르도록 만들자
- public 상속의 의미는 "is-a(...는 ...의 일종)"입니다. 기본 클래스에 적용되는 모든 것들이 파생 클래스에 그대로 적용되어야 합니다. 왜냐하면 모든 파생 클래스 객체는 기본 클래스 객체의 일종이기 때문입니다.
항목 33 : 상속된 이름을 숨기는 일은 피하자
1. 문제
class Base
{
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
...
};
class Derived : public Base
{
public:
virtual void mf1();
void mf3();
void mf4();
...
};
void main()
{
Derived d;
int x;
...
d.mf1();
// 에러! Derived::mf1이 Base::mf1을 가립니다.
d.mf1(x);
d.mf2();
d.mf3();
// 에러! Derived::mf3이 Base::mf3을 가립니다.
d.mf3(x);
}
2. 해결
2.1. using 선언
class Base
{
private:
int x;
public:
virtual void mf1() = 0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
...
};
class Derived : public Base
{
public:
// Base에 있는 것들 중에 mf1과 mf3을 이름으로 가진 것들을
// Derived의 유효범위에서 볼 수 있도록(또 public 멤버로) 만듭니다.
using Base::mf1;
using Base::mf3;
virtual void mf1();
void mf3();
void mf4();
...
};
void main()
{
Derived d;
int x;
...
d.mf1();
// 정상! Base::mf1을 호출합니다.
d.mf1(x);
d.mf2();
d.mf3();
// 정상! Base::mf3을 호출합니다.
d.mf3(x);
}
2.2. 전달 함수(forwarding function)
class Base
{
public:
virtual void mf1() = 0;
virtual void mf1(int);
// 예전과 동일
...
};
class Derived : private Base
{
public:
// 전달 함수. 임시적으로 인라인 함수가 됩니다.
virtual void mf1()
{
Base::mf1();
}
...
};
void main()
{
...
Derived d;
int x;
// 정상! Derived::mf1(매개변수 없는 버전)을 호출
d.mf1();
// 에러! Base::mf1()은 가려져 있습니다.
d.mf1(x);
}
- 파생 클래스의 이름은 기본 클래스의 이름을 가립니다. public 상속에서는 이런 이름 가림 현상은 바람직하지 않습니다.
- 가려지 이름을 다시 볼 수 있게 하는 방법으로, using 선언 혹은 전달 함수를 쓸 수 있습니다.
항목 34 : 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자
1. 순수 가상 함수
순수 가상 함수를 선언하는 목적은 파생 클래스에서 함수의 인터페이스만 물려주는 것입니다.
class Shape
{
public:
// 설계자 : "draw 함수는 사용자가 직접 제공하도록 하십시오.
// 사용자가 어떻게 구현할지에 대해서 난 아무 생각 없습니다."
virtual void draw() const = 0;
...
};
2. 단순 가상 함수
단순 가상 함수를 선언하는 목적은 파생 클래스로 하여금 함수의 인터페이스뿐만 아니라 그 함수의 기본 구현도 물려받게 하자는 것입니다.
class Shape
{
public:
// 설계자 : "error 함수는 사용자가 지원해야 합니다.
// 만약 새로 만들 생각이 없다면 Shape 클래스에 있는
// 기본 버전을 그대로 쓰셔도 됩니다."
virtual void error(const std::string&msg);
...
};
3. 비가상 함수
비가상 함수를 사용하는 목적은 파생 클래스가 함수 인터페이스와 더불어 그 함수의 필수적인 구현(mandatory implementation)을 물려받게 하는 것입니다.
class Shape
{
public:
// 설계자 : "Shape 및 이것에서 파생된 모든 객체는 이 함수를 사용하세요.
// 이 함수의 동작은 기본 클래스에서 결정되고 파생 클래스에서는 이것을 바꿀 수 없습니다."
int objectID() const;
...
};
80-20 법칙 : 전체 실행 시간의 80%가 소모되는 부분이 전체 코드의 20%밖에 되지 않는다는 법칙.
가상 함수의 비용을 걱정하기 전에 진짜 차이를 만들 수 있는 20%의 코드에 집중하자.
- 인터페이스 상속은 구현 상속과 다릅니다. public 상속에서, 파생 클래스는 항상 기본 클래스의 인터페이스를 모두 물려받습니다.
- 순수 가상 함수는 인터페이스 상속만을 허용합니다.
- 단순(비순수) 가상 함수는 인터페이스 상속과 더불어 기본 구현의 상속도 가능하도록 지정합니다.
- 비가상 함수는 인터페이스 상속과 더불어 필수 구현의 상속도 가하도록 저정합니다.
- James Song
'Books_tech' 카테고리의 다른 글
다시 읽는 <Effective C++> Chapter 6 요약 (3) (0) | 2015.12.01 |
---|---|
다시 읽는 <Effective C++> Chapter 6 요약 (2) (0) | 2015.11.24 |
다시 읽는 <Effective C++> Chapter 5 요약 (2) (0) | 2015.11.12 |
다시 읽는 <Effective C++> Chapter 5 요약 (1) (1) | 2015.11.09 |
다시 읽는 <Effective C++> Chapter 4 요약 (0) | 2015.11.06 |
- #레거시코드
- #제럴드와인버그
- #알고리즘
- #마이클페더스
- #스콧마이어스
- 책
- #자녀교육
- #세미나
- #임백준
- Scott Meyers
- #uwp
- #로버트마틴
- #build2016
- #scottmeyers
- Effective C++
- #EffectiveModernCpp
- #ndc
- #ModernCPP
- #클린코드
- #mva
- #cplusplus
- 상속
- 객체 지향 설계
- #csharp
- #코드최적화
- #프로그래밍심리학
- Effective Modern C++
- #cpp
- #팀개발
- #techdays2015
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |