[cpp] virtual - override

minjubyeon·2025년 5월 19일

cpp

목록 보기
8/26

0. Polymorphism

부모 클래스의 포인터나 참조를 통해 여러 자식 클래스들을 동일하게 다루면서도 각 자식의 고유한 동작을 실행시키는 것
→ 이게 다형성(polymorphism)이고, 그 핵심이 override


#include <iostream>
class Base {
	int x;
public:
	Base(int x = 0) : x(x) {};
	virtual void Print() const { std::cout << x << ' '; }
};

class Derived : public Base {
	int x;
public:
	Derived(int x1 = 0, int x2 = 0) : Base(x1), x(x2) {};
	void Print() const override { //⚠️ override
		Base::Print();
		std::cout << x << ' ';
	}
}; 

→ override 키워드를 사용하여 이 함수가 부모 클래스의 가상 함수인 Print를 재정의하도록 하였다.

1.override

override란, 부모 클래스의 가상 함수(virtual function)를 자식 클래스에서 고쳐서 새로 정의하는 것이다. override 키워드는 "내가 이 함수는 재정의하는 거다!"를 명시한다. 주요 목적은 컴파일러가 재정의를 정확하게 수행했는지를 검사하게 하여, 실수를 방지하는 데 있다.

이를 통해 다형성(polymorphism)을 구현할 수 있다

  • Base 클래스의 Print()는 virtual 함수다.
  • Derived 클래스의 Print()는 Base의 Print()를 재정의한다.
  • override 키워드를 붙임으로써, 컴파일러는 해당 함수가 정확히 재정의되는지 확인한다.

🔍 override를 하는 이유?

  • 함수 이름이 틀리거나
  • 매개변수가 다르거나
  • const를 빼먹은 경우

컴파일 에러를 발생시켜, 실수를 미리 방지할 수 있다.
가상 함수를 재정의할 땐 항상 override를 붙이는 것이 좋은 습관이다.

⚠️ 차이점 (override vs 상속)

항목상속override
의미부모의 기능을 그대로 물려받음부모의 가상 함수를 재정의함
키워드 필요 여부없음virtual(부모), override(자식) 필요
실행 시 결정정적 바인딩 (기본적으로)동적 바인딩 (virtual 함수인 경우)
목적코드 재사용동작 변경, 다형성 구현
class Base {
public:
    void Show() { std::cout << "Base Show\n"; }
};

class Derived : public Base {};

Derived는 Show()를 아무것도 안 해도 그대로 사용할 수 있음.
→ 상속


class Base {
public:
    virtual void Show() { std::cout << "Base Show\n"; }
};

class Derived : public Base {
public:
    void Show() override { std::cout << "Derived Show\n"; }
};

→ 이것은 단순히 Show()를 물려받는 게 아니라 고쳐서 다른 동작을 하도록 만든 것, 즉 재정의(override)


🔚 결론

상속은 "부모 거 그냥 쓸게요"
override는 "부모 거 내가 다시 만들게요"


2.override를 해야하는 경우

  1. 부모 클래스의 가상 함수 동작을 자식 클래스에서 다르게 하고 싶을 때

  2. 동일한 부모 클래스를 상속받은 여러 클래스가 각각 다른 동작을 해야 할 때

  3. 부모 포인터로 자식 객체를 다룰 때 정확한 함수가 호출되도록 할 때


3.virtual과 override

#include <iostream>
class Base {
	int x;
public:
	Base(int x = 0) : x(x) {};
	virtual void Print() const { std::cout << x << ' '; } 
    // ⚠️ virtual
};
class Derived : public Base {
	int x;
public:
	Derived(int x1 = 0, int x2 = 0) : Base(x1), x(x2) {};
	void Print() const override {
    // ⚠️ override
		Base::Print();
		std::cout << x << ' ';
	}
};
int main() {
	Derived d1(1, 2);
	Base& b1 = d1; // ok, reference to a derived class
	d1.Print(); // 1 2
	b1.Print(); // 1 2
} 

→ Base 타입의 참조 변수 b1이 Derived 객체를 참조하고 있으므로, virtual 함수인 Print()가 오버라이딩된 Derived의 Print()를 호출하며, 이 함수는 먼저 Base::Print()로 정수형 멤버 변수 x를 출력하고, 이어서 Derived의 멤버 변수 x를 출력하여 최종적으로 1 2를 출력한다.

b1은 겉모습은 Base지만 실제로 가리키는 객체는 Derived 타입이다. 즉, b1이 바라보는 실제 데이터는 Derived 객체다. 따라서 함수의 override가 필요하다.

🔍 override 되지 않았다면?

→ Base 클래스의 Print()만 호출되고, Derived의 x는 출력되지 않는다. 즉 1만 출력한다.


🚨 핵심 키워드: virtual

C++에서 virtual 함수는 런타임(dynamic) 바인딩을 사용한다.

  • Base 타입의 포인터나 참조를 통해 자식 객체를 조작하더라도, 실제 객체의 타입(Derived)에 따라 적절한 함수가 호출된다.

override만 있다고 자식 함수가 호출되는 게 아니다.

  • 부모 함수가 virtual일 때만, Base 타입 참조나 포인터를 통해서도 자식 클래스의 재정의된 함수가 호출된다.

  • b1.Print()에서 Derived::Print()가 호출된 건, Base::Print()가 virtual로 선언되어 있었기 때문이다. override는 그 위에 안전하게 덧붙인 보증 장치일 뿐이다.

+) override의 역할
자식 클래스가 Print()를 다시 정의하면서 override를 붙이면, 컴파일러가 이 함수가 부모의 virtual 함수와 정확히 매칭되는지 검사한다. override가 있어도 실제로 중요한 건 부모가 virtual을 붙어있지 않으면 override 되지 않는다.

profile
안녕하세요.

0개의 댓글