1. 組合與繼承
例子
// 組合
class ClassA {
ClassB member;
};
// 繼承
class BaseClass {
};
class DerivedClass : public ClassA {
};
注意點(diǎn):
- 構(gòu)造順序:由內(nèi)而外
- 析構(gòu)順序:由外而內(nèi)
三種繼承方式
- 公有繼承(public)
公有繼承的特點(diǎn)是基類的公有成員和保護(hù)成員作為派生類的成員時(shí),它們都保持原有的狀態(tài)肚邢,而基類的私有成員仍然是私有的淘菩,不能被這個(gè)派生類的子類所訪問(wèn)阔拳。 - 私有繼承(private)
私有繼承的特點(diǎn)是基類的公有成員和保護(hù)成員都作為派生類的私有成員燎含,并且不能被這個(gè)派生類的子類所訪問(wèn)。 - 保護(hù)繼承(protected)
保護(hù)繼承的特點(diǎn)是基類的所有公有成員和保護(hù)成員都成為派生類的保護(hù)成員内贮,并且只能被它的派生類成員函數(shù)或友元訪問(wèn)栖疑,基類的私有成員仍然是私有的讨永。
2. 虛函數(shù)與多態(tài)
例子
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函數(shù)滔驶,父類的被調(diào)用遇革;virtual函數(shù),子類被調(diào)用
t->func(); // derived
t->func2(); // base
delete t;
}
注意點(diǎn):
- 析構(gòu)函數(shù)要寫(xiě)成虛函數(shù)揭糕,這樣子類才能被正確地釋放內(nèi)存萝快;
- 父類定義成virtual的函數(shù),子類即使不寫(xiě)virtual關(guān)鍵字著角,該函數(shù)也是virtual的揪漩;不過(guò)為了可讀性好,子類最好也寫(xiě)上virtual吏口;
- 對(duì)于父類virtual函數(shù)奄容,子類如果有同名但不同型的函數(shù),則構(gòu)成overload产徊,而不是override父類的virtual函數(shù)昂勒;
- C++11起子類復(fù)寫(xiě)父類的virtual函數(shù)時(shí)可以在函數(shù)尾加上override關(guān)鍵字,編譯器會(huì)幫忙檢查是否正確復(fù)寫(xiě)舟铜。
class BaseClass {
public:
virtual void func() { puts("base"); }
};
class DerivedClass : public ClassA {
public:
virtual void func() override { puts("derived"); }
};
虛函數(shù)實(shí)現(xiàn)原理
每當(dāng)創(chuàng)建一個(gè)包含有虛函數(shù)的類或從包含有虛函數(shù)的類派生一個(gè)類時(shí)戈盈,編譯器就為這個(gè)類創(chuàng)建一個(gè)虛函數(shù)表:VTABLE,這個(gè)表中存儲(chǔ)了這個(gè)類及它的基類中所有聲明為virtual函數(shù)的地址谆刨,如果這個(gè)派生類有復(fù)寫(xiě)父類的virtual函數(shù)塘娶,則放置的是派生類中的函數(shù)地址,如果沒(méi)有痊夭,則放置的是父類中的函數(shù)地址刁岸。對(duì)于每一個(gè)該類對(duì)象,在對(duì)象體中都會(huì)含有一個(gè)指向該VTABLE的指針她我,用于查找VTABLE中的函數(shù)地址虹曙,該指針被成為VPTR膝宁,通常位于對(duì)象的頭部。
class Base {
virtual void f();
virtual void g();
virtual void h();
};
class Derive {
virtual void f();
virtual void g1();
virtual void h1();
};
3. 委托設(shè)計(jì)
例子
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();
// ...
}