방프리

17.12.15. Effective C++ 6. 상속, 그리고 객체 지향 설계 (항목33) 본문

C++/Effective C++

17.12.15. Effective C++ 6. 상속, 그리고 객체 지향 설계 (항목33)

방프리 2020. 1. 17. 08:10

항목 33: 상속된 이름을 숨기는 일은 피하자

 

이 세상에 존재하는 모든 것들은 이름을 가지고 있습니다. 사람들도 각각 이름을 가지고 있으며

동물, 식물들도 모두 이름을 가지고 있죠. 이번 항목에서는 모든 존재하는 것들이 이름을 가지듯이

프로그래밍에서도 이름이 중요하다는 것을 필자는 강조하고 있습니다. 일단 예제 코드를 먼저 보겠습니다.

ex1)

int x; // 전역 변수

 

void someFunc()

{

double x; //지역 변수

std::cin >> x; //입력을 받아, 지역변수 x에 새 값을 읽어 넣는다.

}

 

어떤 변수에 사용이 될까요?? 전역 변수인 x? 아님 지역변수인 x???

정답은 바로 지역변수인 x입니다. 이유가 궁금하신 분들을 위해 그림으로 간단하게 표현해보겠습니다.

(그림 1-1)

컴파일러는 변수에 대한 참조를 찾을 때 최초 자신의 지역 범위부터 이름을 찾고 없으면 전역으로 찾습니다.

그러니 지역변수인 x로 인해 전역변수 x가 가려지게 되는 것이죠. 이건 데이터 타입이 다르다고 해서 

바뀌지 않습니다. 컴파일러가 변수를 찾을 때 어떻게 찾는지 대충 파악했으니 클래스에서는 어떻게 되는지

살펴볼까요?

 

ex2)

 

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

...

};

 

다음 예제를 그림으로 표현하면 이렇게 할 수 있겠네요.

(그림 1-2)

 

Derived 클래스에서 Base 클래스를 모두 상속하지만, Derived 클래스에서 함수 m1()이 정의되어 있다면,

나머지는 이름을 가리게 됩니다. 또한 순수 가상함수의 경우 상속받은 파생 클래스에 정의하지 않으면

컴파일 에러를 뱉어버리죠

그럼 다음 예제를 살펴보겠습니다.

 

ex3)

class Base

{

private:

int x;

public:

virtual void mf1() = 0;

virtual void mf1(int a)

{

std::cout << "Base의 mf(int) 호출" << std::endl;

}

};

 

class Derived : public Base

{

public:

using Base::mf1;

virtual void mf1()

{

std::cout << "Derived의 mf1(int) 호출" << std::endl;

}

};

 

(그림 1-3)

3번 예제는 다음과 같이 표현할 수 있겠네요. 이번엔 아예 using을 통해 직접 클래스를 명시해줍니다.

이렇게 한다면 가려질 염려나 이름을 소실할 걱정이 사라지게 되죠

코딩을 하는 프로그래머 입장에서도 읽기에도 굉장히 명확하구요

이것으로 파생클래스에서 부모클래스에 대한 내용을 명시할 때 사용할 수 있는 방법을 알아봤습니다.

이번 내용은 앞의 내용과 조금 다르지만 참고해볼만한 내용입니다. 예제 먼저 볼게요

 

ex4)

 

class Base

{

public:

virtual void mf1() = 0;

virtual void mf1(int);

...

};

 

class Derived : private Base

{

public:

virtual void mf1() // 전달 함수, 암시적으로 인라인 함수가 됩니다.

{ Base::mf1(); }

...

};

 

만약 클래스를 상속했는데 쓰고 싶지 않은 항목이 있다면 public 상속을 포기하면 됩니다.

하지만 이렇게 되면 is-a 관계가 깨질 뿐더러 상속을 하는 의미가 없어져버리죠 

그럴 때 private 상속을 이용하는 것입니다. 만약 Derived 클래스가 Base 클래스로부터

private 상속이 이루어진다고 가정하에 Derived 클래스가 Base 클래스에서 상속하고 싶은 것이

mf1() 하나 뿐이라면 using선언으로는 해결할 수 없습니다. using 선언은 그 이름에

해당되는 것들을 모두 파생 클래스에 내려버리기 때문이죠

그래서 나온 것이 바로 전달 함수(forwarding function)입니다. 예제 4번에 있는 것이죠

 

이것만은 잊지 말자!

 

* 파생 클래스의 이름은 기본 클래스의 이름을 가립니다. public 상속에서는 이런 이름 

가림 현상은 바람직하지 않습니다.

* 가려진 이름을 다시 볼 수 있게 하는 방법으로, using 선언 혹은 전달 함수를 쓸 수 있습니다.

Comments