https://www.learncpp.com/cpp-tutorial/calling-inherited-functions-and-overriding-behavior/
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로 넘어갔다
하지만 만약 우리고 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으로 간주하므로 불가능하다
때로는 우리는 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로 지정된 함수를 위와 같이 재정의 하고 싶다면 어떻게 해야할까?
#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