1. 組合與繼承
例子
// 組合
class ClassA {
ClassB member;
};
// 繼承
class BaseClass {
};
class DerivedClass : public ClassA {
};
注意點:
- 構造順序:由內而外
- 析構順序:由外而內
三種繼承方式
- 公有繼承(public)
公有繼承的特點是基類的公有成員和保護成員作為派生類的成員時,它們都保持原有的狀態,而基類的私有成員仍然是私有的,不能被這個派生類的子類所訪問。 - 私有繼承(private)
私有繼承的特點是基類的公有成員和保護成員都作為派生類的私有成員,并且不能被這個派生類的子類所訪問。 - 保護繼承(protected)
保護繼承的特點是基類的所有公有成員和保護成員都成為派生類的保護成員,并且只能被它的派生類成員函數或友元訪問,基類的私有成員仍然是私有的。
2. 虛函數與多態
例子
class BaseClass {
public:
virtual void func() { puts("base"); }
void func2() { puts("base"); }
virtual ~BaseClass() {}
};
class DerivedClass : public ClassA {
public:
virtual void func() { puts("derived"); }
void func2() { puts("derived"); }
virtual ~DerivedClass() {}
};
void test() {
BaseClass* t = new DerivedClass;
// 非virtual函數,父類的被調用;virtual函數,子類被調用
t->func(); // derived
t->func2(); // base
delete t;
}
注意點:
- 析構函數要寫成虛函數,這樣子類才能被正確地釋放內存;
- 父類定義成virtual的函數,子類即使不寫virtual關鍵字,該函數也是virtual的;不過為了可讀性好,子類最好也寫上virtual;
- 對于父類virtual函數,子類如果有同名但不同型的函數,則構成overload,而不是override父類的virtual函數;
- C++11起子類復寫父類的virtual函數時可以在函數尾加上override關鍵字,編譯器會幫忙檢查是否正確復寫。
class BaseClass {
public:
virtual void func() { puts("base"); }
};
class DerivedClass : public ClassA {
public:
virtual void func() override { puts("derived"); }
};
虛函數實現原理
每當創建一個包含有虛函數的類或從包含有虛函數的類派生一個類時,編譯器就為這個類創建一個虛函數表:VTABLE,這個表中存儲了這個類及它的基類中所有聲明為virtual函數的地址,如果這個派生類有復寫父類的virtual函數,則放置的是派生類中的函數地址,如果沒有,則放置的是父類中的函數地址。對于每一個該類對象,在對象體中都會含有一個指向該VTABLE的指針,用于查找VTABLE中的函數地址,該指針被成為VPTR,通常位于對象的頭部。
class Base {
virtual void f();
virtual void g();
virtual void h();
};
class Derive {
virtual void f();
virtual void g1();
virtual void h1();
};
父類虛函數表
子類虛函數表
3. 委托設計
例子
class Delegate {
virtual func() = 0;
};
class TestClass {
Delegate* delegate = nullptr;
public:
void setDelegate(Delegate* d) { delegate = d; }
void test() { if (delegate) delegate->func() };
};
class ImplClassA : public Delegate {
virtual func() { puts("delegate class A"); }
};
class ImplClassB : public Delegate {
virtual func() { puts("delegate class B"); }
};
void test() {
TestClass a;
a.setDelegate(new ImplClassA);
a.test();
a.setDelegate(new ImplClassB);
a.test();
// ...
}