1. 함수와 테이블
1) 멤버 변수와 멤버 함수의 차이
- 멤버 변수는 객체마다 별도로 소유된다.
- 멤버 함수는 클래스가 정의한 하나의 내용을 모든 객체가 공유한다.
- 객체마다 같은 동작을 하게 하기 위해 함수는 공유 구조를 갖는다.
2) 가상 함수(Virtual Function)
- 가상 함수는 상속 관계에서 오버라이딩(재정의)을 통해 다형성을 구현하는 함수이다.
- virtual 키워드를 사용하여 선언한다.
- 객체 포인터나 참조를 통해 호출할 경우, 실제 객체 타입에 맞는 함수가 실행된다.
3) 가상 함수 테이블(Virtual Table, V-Table)
- 클래스가 가상 함수를 하나라도 포함하면, 컴파일러는 가상 함수 테이블(V-Table)을 생성한다.
- V-Table은 일종의 내부 구조로, 가상 함수 호출 시 어떤 함수가 실행되어야 하는지를 결정한다.
- 직접 접근할 수는 없지만, 실행 흐름에는 필수적으로 사용된다.
⭕ V-Table의 개념적 구조
클래스: Animal
----------------------
가상 함수 목록:
- speak() → 0x001F3B20
- move() → 0x002A1744
클래스: Dog (Animal 상속)
----------------------
가상 함수 목록:
- speak() → 0x003D1020 // 오버라이딩된 주소
- move() → 0x002A1744 // 기본 상속된 주소
· Key는 함수 식별자, Value는 해당 함수의 메모리 주소이다.
· 객체 생성과 관계없이, 클래스 수준에서 V-Table은 메모리에 먼저 할당된다.
· 함수 호출 시, 객체 내부의 **vptr(가상 함수 테이블 포인터)**를 통해 테이블을 참조한다.
· 이 과정을 거치므로, ⚠️ C++은 C에 비해 실행 속도가 조금 느릴 수 있다.
2. 다중 상속과 가상 상속
1) 다중 상속(Multiple Inheritance)
- 다중 상속이란, 하나의 클래스가 두 개 이상의 기초 클래스(Base Class)로부터 상속받는 구조이다.
- 유연한 구조를 설계할 수 있지만, ⚠️ 구조가 복잡해지고 문제 발생 가능성이 높다.
2) 다중 상속의 문제점 - 모호성(Ambiguity)
- 여러 기초 클래스에 동일한 이름의 멤버가 존재할 경우, 호출 시 어떤 클래스의 멤버를 참조할지 불분명해진다.
- 이 경우, 기초클래스명::멤버명 형식으로 명시적으로 지정해야 한다.
⭕ 예시
class A { int value = 1; };
class B : public A {};
class C : public A {};
class D : public B, public C {};
D d;
d.value; // ⚠️ 오류 발생: A::value가 B와 C에 모두 존재하여 모호함
// 해결 방법: d.B::value; // 또는 d.C::value;
3) 가상 상속(Virtual Inheritance)
- 가상 상속은 중복 상속 문제를 해결하기 위한 구조이다.
- virtual 키워드를 사용하여 기초 클래스를 가상으로 상속받는다.
- 유도 클래스가 여러 중간 클래스를 통해 같은 기초 클래스를 상속받더라도, ⚠️ 하나의 기초 클래스만 공유되도록 처리된다.
⭕ 가상 상속 전/후 비교 예시
📌 [가상 상속을 사용하지 않은 경우]
class A { int value; };
class B : public A {};
class C : public A {};
class D : public B, public C {};
// D 객체는 A 클래스를 두 번 상속받게 되어
// D 안에는 A::value가 두 개 존재함 → ⚠️ 모호성 발생
📌 [가상 상속을 사용한 경우]
class A { int value; };
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
// D 객체는 A를 단 한 번만 상속받음 → ✅ 모호성 제거
✅ 마무리 정리
1️⃣ 멤버 변수는 각 객체가 따로 가지고, 멤버 함수는 객체들이 공유한다.
2️⃣ 가상 함수는 virtual 키워드를 사용하며, 다형성을 구현하는 데 사용된다.
3️⃣ 가상 함수가 포함된 클래스는 **컴파일 시 V-Table(가상 함수 테이블)**을 생성한다.
4️⃣ V-Table은 함수 이름(Key)과 함수 주소(Value)로 구성되며, 간접 호출 구조를 가진다.
5️⃣ 다중 상속은 하나의 클래스가 여러 기초 클래스를 상속받는 구조이며, ⚠️ 모호성 문제를 유발할 수 있다.
6️⃣ 모호성은 기초클래스명::멤버명으로 명시하여 해결할 수 있다.
7️⃣ **가상 상속(Virtual Inheritance)**은 virtual 키워드를 사용하여 중복 상속 문제를 방지한다.
8️⃣ 가상 상속을 사용하면 여러 경로로 상속받은 기초 클래스의 인스턴스를 하나만 공유하게 된다.
9️⃣ C++은 가상 함수 호출 시 테이블을 간접 참조하므로, C보다 속도가 느릴 수 있다.
🔟 다중 상속과 가상 상속은 유연한 구조를 제공하지만, ⚠️ 설계 시 주의가 필요하다.
💻 예제 코드 - 가상 함수와 가상 상속
#include <iostream>
using namespace std;
// 기초 클래스
class Animal {
public:
virtual void speak() { // 가상 함수 선언
cout << "Animal sound\n";
}
};
// 중간 클래스 1 - 가상 상속
class Dog : virtual public Animal {
public:
void speak() override { // 오버라이딩
cout << "Woof!\n";
}
};
// 중간 클래스 2 - 가상 상속
class Cat : virtual public Animal {
public:
void speak() override {
cout << "Meow!\n";
}
};
// 최종 클래스 - 다중 상속
class Pet : public Dog, public Cat {
public:
// speak()를 다시 정의하지 않으면 ambiguous 발생 가능
void speak() override {
cout << "Mixed pet sound\n";
}
};
int main() {
Pet myPet; // Pet 객체 생성
Animal* ptr = &myPet; // 기초 클래스 포인터로 참조
ptr->speak(); // 가상 함수 호출: 실제 객체 타입(Pet)의 speak() 실행됨
// 모호성 없는 직접 호출
myPet.Dog::speak(); // "Woof!" 출력
myPet.Cat::speak(); // "Meow!" 출력
return 0;
}
🧾 실행 결과
Mixed pet sound
Woof!
Meow!
🔎 코드 설명
- Animal은 가상 함수 speak()를 가진 기초 클래스이다.
- Dog, Cat은 Animal을 가상 상속하여 Pet이 상속 시 Animal을 하나만 공유한다.
- Pet에서 speak()를 오버라이딩하지 않으면, 어떤 speak()를 호출할지 모호해진다.
- 가상 함수 호출 시, 실제 객체 타입에 따라 Pet의 speak()가 호출된다.
- 각 경로의 speak()는 기초클래스명::함수명() 형식으로 명확히 호출할 수 있다.
'Language > C++' 카테고리의 다른 글
| 📌 Chapter 10 - 기본 대입 연산자 오버로딩 (0) | 2025.09.08 |
|---|---|
| 📌 Chapter 10 – 연산자 오버로딩 (0) | 2025.09.03 |
| 📌 Chapter 08 - 객체 포인터와 가상 함수의 이해 (1) | 2025.08.28 |
| 📌Chapter 07 - 상속 (3) | 2025.08.27 |
| 📌 Chapter 06 - 특수 멤버와 한정자(Special Member & Specifier) (1) | 2025.08.25 |