17.7 Calling inherited functions and overriding behavior

주홍영·2022년 3월 20일
0

Learncpp.com

목록 보기
168/199

https://www.learncpp.com/cpp-tutorial/calling-inherited-functions-and-overriding-behavior/

Calling a base class function

derived class object의 member function을 호출한다면,
컴파일러는 먼저 derived class에 member function이 있는지 찾아볼 것이다
만약 존재하지 않는다면 inheritance chain을 타고 올라고 member가 존재하는지 찾아볼 것이다

class Base
{
protected:
    int m_value {};

public:
    Base(int value)
        : m_value { value }
    {
    }

    void identify() const { std::cout << "I am a Base\n"; }
};

class Derived: public Base
{
public:
    Derived(int value)
        : Base { value }
    {
    }
};
int main()
{
    Base base { 5 };
    base.identify();

    Derived derived { 7 };
    derived.identify();

    return 0;
}

위의 출력의 결과는 다음과 같다

I am a Base
I am a Base

main에서 derived.identify가 호출되면 Derived에서 찾아보고 존재하지 않기에 Base로 넘어갔다

Redefining behaviors

하지만 만약 우리고 Derived class에서 identify를 정의하면 이를 대신 사용한다

class Derived: public Base
{
public:
    Derived(int value)
        : Base { value }
    {
    }

    int getValue() const { return m_value; }

    // Here's our modified function
    void identify() const { std::cout << "I am a Derived\n"; }
};

위와 같이 identify를 재정의 했다
따라서 출력은 다음과 같다

I am a Base
I am a Derived

재정의된 함수는 재정의된 access specifier를 따르게 된다
따라서 Base에서 private 함수 였어도
Derived에서 public으로 재정의 되면 호출에 문제가 없다

반대로 Base에서는 public 이더라도
Derived에서 private이라면 Derived object에서 접근할 때 private으로 간주하므로 불가능하다

Adding to existing functionality

때로는 우리는 Base의 functionality를 유지한 채로 Derived만의 기능을 덧붙이고 싶을 때가 있다
앞서 말한 사례는 모두 Derived::identify()가 Base::identify()를 hide하는 경우였다
만약 기능을 조금 추가하고 싶은 경우라면 다음과 같이 재정의할 수 있다

class Derived: public Base
{
public:
    Derived(int value)
        : Base { value }
    {
    }

    int getValue() const  { return m_value; }

    void identify() const
    {
        Base::identify(); // call Base::identify() first
        std::cout << "I am a Derived\n"; // then identify ourselves
    }
};

Base::identify()를 호출하기에 기능을 추가할 수 있게 되었다

그런데 우리는 왜 scope resolution operator(::)(범위지정연산자)가 필요한 것일까?
만약 Base::를 prefix로 하지 않는다면 다음과 같다

class Derived: public Base
{
public:
    Derived(int value)
        : Base { value }
    {
    }

    int getValue() const { return m_value; }

    void identify() const
    {
        identify(); // Note: no scope resolution!
        cout << "I am a Derived";
    }
};

이때 Derived::identify내부에서 identify()를 호출하는데
여기서 Base::가 없으므로 자동으로 identify()는 Derived::identify를 호출한다
따라서 무한재귀가 되는 것이다

friend

만약 friend로 지정된 함수를 위와 같이 재정의 하고 싶다면 어떻게 해야할까?

#include <iostream>

class Base
{
private:
	int m_value {};

public:
	Base(int value)
		: m_value{ value }
	{
	}

	friend std::ostream& operator<< (std::ostream& out, const Base& b)
	{
		out << "In Base\n";
		out << b.m_value << '\n';
		return out;
	}
};

class Derived : public Base
{
public:
	Derived(int value)
		: Base{ value }
	{
	}

	friend std::ostream& operator<< (std::ostream& out, const Derived& d)
	{
		out << "In Derived\n";
		// static_cast Derived to a Base object, so we call the right version of operator<<
		out << static_cast<const Base&>(d);
		return out;
	}
};

int main()
{
	Derived derived { 7 };

	std::cout << derived << '\n';

	return 0;
}

위의 사례는 operator overloading을 redefined한 것이다
그런데 Base의 functioanlity도 사용하고 싶기에 static_cast를 이용해 추가해주고 있다
따라서 출력은 다음과 같다

In derived
In base
7

profile
청룡동거주민

0개의 댓글