類型準換與繼承
為了支持c++的多態性,才用了動態綁定和靜態綁定。
需要理解四個名詞:
- 對象的靜態類型:對象在聲明時采用的類型,是在編譯期確定的。
- 對象的動態類型:目前所指對象的類型,是在運行期決定的。對象的動態類型可以更改,但是靜態類型無法更改。
關于對象的靜態類型和動態類型,看一個示例:
class B {}class C : public B {}class D : public B {}D* pD = new D(); // pD的靜態類型是它聲明的類型D*,動態類型也是D*
B* pB = pD; // pB的靜態類型是它聲明的類型B*,動態類型是pB所指向的對象pD的類型D*
C* pC = new C();
pB = pC; // pB的動態類型是可以更改的,現在它的動態類型是C*
?動態綁定與靜態綁定:
- 靜態綁定:又名前期綁定,綁定的是靜態類型,所對應的函數或屬性依賴于對象的靜態類型,發生在編譯期;
- 動態綁定:又名后期綁定,綁定的是動態類型,所對應的函數或屬性依賴于對象的動態類型,發生在運行期;
?比如常見的,virtual函數是動態綁定,non-virtual函數是靜態綁定,缺省參數值也是靜態綁定。
class B {
public:void DoSomething();virtual void vfun();
}class C : public B {
public: //首先說明一下,這個子類重新定義了父類的no-virtual函數,這是一個不好的設計,會導//致名稱遮掩;這里只是為了說明動態綁定和靜態綁定才這樣使用。 virtual void vfun(); void DoSomething();
}class D : public B {
public:void DoSomething();virtual void vfun();
}D* pD = new D();
B* pB = pD;
【問題】pD->DoSomething()?和?pB->DoSomething()?調用的是同一個函數嗎?
不是,雖然pD和pB都指向同一個對象。因為函數DoSomething是一個no-virtual函數,它是靜態綁定的,也就是編譯器會在編譯期根據對象的靜態類型來選擇函數。pD的靜態類型是D*,那么編譯器在處理pD->DoSomething()的時候會將它指向D::DoSomething()。同理,pB的靜態類型是B*,那pB->DoSomething()調用的就是B::DoSomething()。
?
【問題】pD->vfun()?和pB->vfun()?調用的是同一個函數嗎?
是的,因為vfun是一個虛函數,它動態綁定的,也就是說它綁定的是對象的動態類型,pB和pD雖然靜態類型不同,但是他們同時指向一個對象,他們的動態類型是相同的,都是D*,所以,他們的調用的是同一個函數:D::vfun()。
?
【注意】上面都是針對對象指針的情況,對于引用(reference)的情況同樣適用。
指針和引用的動態類型和靜態類型可能會不一致,但是對象的動態類型和靜態類型是一致的。
?
當缺省參數和虛函數一起出現的時候情況有點復雜,極易出錯。我們知道,虛函數是動態綁定的,但是為了執行效率,缺省參數是靜態綁定的。
class B {
public:virtual void vfun(int i = 10) { cout << "class B: " << i << endl; }
};class D : public B {
public:virtual void vfun(int i = 20) { cout << "class D: " << i << endl; }
};int main()
{D* pD = new D();B* pB = pD;pD->vfun();pB->vfun();return 0;
}
輸出結果:
分析:有上面的分析可知pD->vfun()和pB->vfun()調用都是函數D::vfun(),但是他們的缺省參數是多少?缺省參數是靜態綁定的,pD->vfun()時,pD的靜態類型是D*,所以它的缺省參數應該是20;同理,pB->vfun()的缺省參數應該是10。編寫代碼驗證了一下,正確。
?
抽象基類
#include <iostream>
using namespace std;class A {
public: virtual void out1() = 0;virtual ~A() {};virtual void out2() { cout << "A(out2)" << endl; }void out3() { cout << "A(out3)" << endl; }
};class B : public A {
public:virtual ~B() {};void out1(){ cout << "B(out1)" << endl; }void out2(){ cout << "B(out2)" << endl; }void out3(){ cout << "B(out3)" << endl; }
};int main()
{A *ab = new B;ab->out1();ab->out2();ab->out3();cout << "************************" << endl;B *bb = new B;bb->out1();bb->out2();bb->out3();delete ab;delete bb;return 0;
}
輸出結果:
?
c++如何防止一個類被其他類繼承?
如何在防止一個類被其他的類繼承呢?
如果是僅僅為了達到這個目的可以直接把這個類的構造函數設置成私有的,這樣就杜絕了其他類的繼承。也相當于毀掉了這個類(無法再創造出自己的對象)。
那么怎么樣既要保證這個類的完整性,又防止其他類的繼承呢?
這就要借助友元來實現,因為友元是不可以被繼承的。如果一個類的構造函數要借助它的友元類,那么繼承了這個類的類就無法構造自己的對象。從而杜絕了被繼承。
?
#include <iostream>
using namespace std;
class C;class BASE
{
private:BASE() {}friend class C; //設class C為class BASE的友元
};
class C : public virtual BASE
{
};
class D :public C
{
};int main()
{C c;//D d; 不可以實例化對象
}
為什么class C要虛擬繼承class BASE 而不是直接繼承呢?
參考資料?
- c++如何防止一個類被其他類繼承