[Effective C++] 항목33 : 상속된 이름을 숨기는 일은 피하자

Jangmanbo·2024년 4월 17일
0

Effective C++

목록 보기
33/33

C++의 이름 가리기 규칙

C++의 이름 가리기 규칙에 의하면 안쪽 유효범위에 있는 이름이 바깥쪽 유효범위에 있는 이름을 가린다.
컴파일러는 자신이 처리하고 있는 유효범위에서 같은 이름을 가진 것이 있는지 찾는다.

유효 범위

int x;	// 전역 변수

void someFunc()
{
	double x;	// 지역 변수
    
    cin >> x;
}

cin >> x;에서 x는 지역 변수인 double x 이다. (전역 변수 x의 타입은 중요하지 않다.)

상속

class Base
{
private:
	int x;
    
public:
	virtual void mf1() = 0;
    virtual void mf2();
    void mf3();
    ...
};

class Derived : public Base
{
public:
	virtual void mf1();
    void mf4();
    ...
};

void Derived::mf4()
{
	...
    mf2();
    ...
}

컴파일러가 mf4()에서 호출하는 mf2()가 어느 것인지 파악하는 과정

  1. 지역 유효범위(mf4의 유효범위) 탐색
  2. mf4 유효범위를 감싸는 유효범위(Derived 유효범위)를 찾아 탐색
  3. Derived 유효범위를 감싸는 유효범위(Base 유효범위)를 찾아 탐색 > 찾았다!
    3-1. 만약 3에서도 못찾았다면 Base를 둘러싸는 네임스페이스, 전역 유효범위까지 탐색


가려진 이름 보게하기

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();
    ...
};


Derived d;
int x;

...

d.mf1();		// Success. Derived::mf1 호출
d.mf1(x);		// Error. Derived::mf1이 Base::mf1을 가린다

d.mf2();		// Success. Basse::mf2 호출

d.mf3();		// Success. Derived::mf3 호출
d.mf3(x);		// Error. Derived::mf3이 Base::mf3을 가린다

Base::mf1(int), Base::mf3(double)Derivedmf1, mf3에 의해 가려진다.
이름 가리기 규칙에 의하면 함수의 매개변수 타입의 차이나 가상/비가상 함수 여부와는 상관없이 이름이 가려진다.는 것을 알 수 있다.

public 상속: 오버로드된 함수 상속하기

위의 예시처럼 public 상속에서 기본 클래스의 함수 중 몇 개만 재정의 하면,
기본 클래스의 오버로드된 다른 함수들이 파생 클래스 함수 이름에 가려져 사용할 수 없게 된다.
일반적으로 다른 오버로드된 함수들을 상속받고 싶을 것이며 그렇지 않다면 이는 is-a 관계에도 위반된다.

이런 상황에서 이름 가리기 규칙을 피해보자.

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:
	using Base::mf1;
    using Base::mf3;
    
	virtual void mf1();
    void mf3();
    void mf4();
    ...
};


d.mf1(x);		// Success. Base::mf1 호출
d.mf3(x);		// Success. Base::mf3 호출

이전에는 볼 수 없었던 Base::mf1(int), Base::mf3(double)를 볼 수 있게 되었다.

private 상속: 오버로드된 함수 중 일부만 상속하기

using 선언을 하면 mf1(int)도 사용할 수 있게 되어버린다.
Derivedmf1()만 상속하고 싶다면, private 상속과 전달 함수를 이용한다.

전달 함수

class Base
{
public:
	virtual void mf1() = 0;
    virtual void mf1(int);
    ...
};

class Derived : private Base
{
public:
	virtual void mf1() { Base::mf1(); }		// 인라인 함수이며 전달 함수
    ...
};





정리

  • 파생 클래스의 이름은 기본 클래스의 이름을 가린다. public 상속에서는 바람직하지 않은 현상
  • 가려진 이름을 다시 볼 수 있게 using 또는 전달 함수를 사용

0개의 댓글