[cpp] virtual

minjubyeon·2025년 5월 19일

cpp

목록 보기
7/26
class Base {
	int x;
public:
	Base(int x = 0) : x(x) {}
	void Print() const { std::cout << x << ' '; }
};

class Base1 : virtual public Base {
	int x1;
public:
	Base1(int x1 = 0) : x1(x1) {};
	void Print() const { std::cout << x1 << ' '; }
};

→ virtual 상속은 다중 상속 시 중복 상속 문제(Diamond Problem)를 방지하기 위해 사용된다.

1.virtual 상속 (가상 상속)

class Base {
    int x;
};

class Base1 : public Base { };
class Base2 : public Base { };
class Derived : public Base1, public Base2 { };

이런 구조를 다이아몬드 상속 구조라고 합니다. Derived 클래스는 Base를 두 번 상속받게 되어, 내부적으로 Base의 복사본이 2개 존재한다.


🧨 문제점

Derived 객체 안에는 Base가 두 개 존재해서, 어떤 Base 멤버에 접근할 때 모호성이 생긴다.

Derived d;
d.x = 5; // 에러! Base가 두 개라서 어떤 x인지 모름

✅ 해결책: virtual 상속

class Base1 : virtual public Base { };
class Base2 : virtual public Base { };
class Derived : public Base1, public Base2 { };

virtual 키워드를 붙이면, Derived 클래스는 Base의 인스턴스를 한 번만 상속받습니다. 즉, 공유합니다.


⚠️ virtual 안 썼을 때와의 차이점

항목virtual 안 씀virtual 씀
Base 인스턴스 수상속받은 만큼 (중복됨)1개만 공유
다이아몬드 상속 문제발생 (모호함)발생하지 않음
생성자 호출 방식각각 호출하나만 호출, 그러나 명시적으로 직접 호출 필요

🔍 왜 그런 구조가 필요할까?

처음부터 Base를 두 번 상속받을 일을 안 만들면 되는 거 아닌가?
→ Base를 두 번 상속받는 다이아몬드 상속 구조는 그냥 일부러 그렇게 하는 게 아니라, 두 클래스(A, B)가 공통된 기능을 갖기 위해 Base를 상속하고, 그 두 클래스를 조합해서 새로운 기능을 만들고자 할 때 자연스럽게 생길 수 있습니다.

Ex) Student와 Employee는 각각 Person을 상속

  • StudentEmployee는 Student와 Employee 두 개를 합쳐 사용
  • 이때 Person이 두 번 상속되어 name이 2개 생기므로 모호해짐

→ 그래서 virtual을 사용하면 Person은 딱 1개만 유지됨


🔚 결론

virtual 상속은 주로 다중 상속에서 동일한 기반 클래스를 공유하기 위해 사용합니다.

단일 상속에서는 불필요하며, 다중 상속 시 안전하게 Base 클래스 하나만 공유하고 싶을 때 사용합니다.

필요할 때 쓰는 안전장치라고 생각하면 됩니다.



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

class Dog : public Animal {
public:
    void Speak() override { std::cout << "Dog\n"; }
};

Animal* a = new Dog();
a->Speak(); // "Dog"

2.virtual 함수 (가상 함수)

→ virtual 함수는 다형성(polymorphism)을 위해 사용된다. 함수를 자식 클래스에서 오버라이딩하고, 부모 포인터로 호출해도 자식 함수가 실행되게 합니다.



profile
안녕하세요.

0개의 댓글