이펙티브 C++
https://www.yes24.com/Product/Goods/17525589
페이지 240p ~ 250p
이펙티브 C++(240p ~ 250p)
요약
항목 33: 상속된 이름을 숨기는 일은 피하자
int x; // 전역 변수
void someFunc()
{
double x;
std::cin >> x;
}
- 값을 읽어 x에 넣는 위의 문장에서 실제로 참조하는 x는 전역 변수 x가 아니라 지역 변수 x이다.
- 안쪽 유효범위에 있는 이름이 바깥쪽 유효범위에 있는 이름을 가리기 때문이다.
- 상속 관계에서는 기본 클래스에 속해 있는 것을 파생 클래스 멤버 함수 안에서 참조하는 문장이 있으면 컴파일러는 이 참조 대상을 바로 찾아낼 수 있다.
- 파생 클래스의 유효범위가 기본 클래스의 유효범위 안에 중첩되어 있기 때문이다.
- C++의 상속은 함수의 매개변수 타입이 다르거나 말거나, 함수가 가상 함수인지 비가상 함수인지 여부에 상관없이 이름을 가린다.
- 이는 어떤 라이브러리 혹은 응용프로그램 프레임워크를 이용하여 파생 클래스를 하나 만들 때, 멀리 떨어져 있는 기본 클래스로부터 오버로드 버전을 상속시키는 경우를 막기 위함이다.
// 가려진 이름은 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 유효 범위에서 볼 수 있도록 만듬
using Base::mf1;
using Base::mf3;
virtual void mf1();
void mf3();
void mf4();
};
Derived d;
int x;
d.mf1();
d.mf1(x); // Base::mf1 호출
d.mf2();
d.mf3();
d.mf3(x); // Base::mf3 호출
- 어떤 기본 클래스로부터 상속을 받으려고 하는데, 오버로드된 함수가 그 클래스에 들어 있고(mf1, mf3) 이 함수들 중 몇 개만 재정의하고 싶다면, 각 이름에 대해 using 선언을 붙여 주어야 한다.
- 경우에 따라 기본 클래스가 가진 함수를 전부 상속 받는 것이 아니라 일부만 받고 싶을 수 있다. 이 경우에는 public 상속 대신 private 상속을 사용해야 하며, using을 사용할 수 없기 때문에 전달 함수를 만들어 사용해야 한다.
- using을 사용하면 그 이름에 해당하는 것들이 모두 파생 클래스로 내려간다.
- inline 전달 함수를 사용하면 기본 클래스의 이름을 파생 클래스의 유효범위에 끌어와 쓰고 싶은데, using 선언을 지원하지 못하는 컴파일러의 경우 우회할 수 있는 경로가 생긴다.
class Base {
public:
virtual void mf1() = 0;
virtual void mf1(int);
};
class Derived : private Base {
public:
// 전달 함수 사용. 암시적으로 인라인으로 변환됨
virtual void mf1() { Base::mf1(); }
};
항목 34: 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자
- public 상속은 함수 인터페이스의 상속과 함수 구현의 상속 두 가지 측면에서 볼 수 있다.
- 클래스 설계자의 입장에서 보면, 멤버 함수의 인터페이스(선언)만을 파생 클래스에 상속받고 싶을 때가 있고, 어쩔 때는 함수의 인터페이스 및 구현을 모두 상속받고 싶은 경우가 있다.
class Shape {
public:
virtual void draw() const = 0;
virtual void error(const std::string& msg);
int objectID() const;
};
class Rectangle : public Shape {};
class Ellipse : public Shape {};
- Shape는 추상 클래스이다.
- 순수 가상 함수인
draw
가 있기 때문이다. - Shape 클래스는 인스턴스를 만들 수 없고, 이 클래스의 파생 클래스만 인스턴스를 만들 수 있다.
- 멤버 함수 인터페이스는 항상 상속되게 되어 있기 때문에 기본 클래스에서 동작하는 함수는 그 클래스의 파생 클래스에서도 동작해야 한다.
- 순수 가상 함수인
- 순수 가상 함수는 다음과 같은 특징을 가진다.
- 어떤 순수 가상 함수를 물려받은 구현 클래스가 해당 순수 가상 함수를 다시 선언해야 한다.
- 순수 가상 함수는 추상 클래스 안에서 정의를 갖지 않는다.
- 따라서 순수 가상 함수는 파생 클래스에게 함수의 인터페이스만을 물려주기 위해 선언한다.
- 단순 가상 함수는 다음과 같은 특징을 가진다.
- 단순 가상 함수는 파생 클래스로 하여금 함수의 인터페이스를 상속하게 한다는 점은 순수 가상 함수와 같다.
- 하지만 단순 가상 함수는 파생 클래스 쪽에서 오버라이드할 수 있는 함수 구현부도 함께 제공한다.
- 비가상 함수는 파생 클래스 쪽에서 이 함수를 재정의할 수 없도록 만들어 준다.
메모
항목 33: 상속된 이름을 숨기는 일은 피하자
- 파생 클래스의 이름은 기본 클래스의 이름을 가린다. public 상속에서는 이런 이름 가림 현상은 바람직하지 않다.
- 가려진 이름을 다시 볼 수 있게 하는 방법으로 using 선언 혹은 전달 함수를 쓸 수 있다.
'책 > 이펙티브 C++' 카테고리의 다른 글
이펙티브 C++(250p ~ 261p) (0) | 2024.01.31 |
---|---|
이펙티브 C++(230p ~ 239p) (0) | 2024.01.30 |
이펙티브 C++(230p ~ 239p) (0) | 2024.01.29 |
이펙티브 C++(210p ~ 220p) (2) | 2024.01.26 |
이펙티브 C++(201p ~ 210p) (0) | 2024.01.25 |