【C++ Priemr | 15】面向對象程序設計

類型準換與繼承

為了支持c++的多態性,才用了動態綁定和靜態綁定。

需要理解四個名詞:

  1. 對象的靜態類型:對象在聲明時采用的類型,是在編譯期確定的。
  2. 對象的動態類型:目前所指對象的類型,是在運行期決定的。對象的動態類型可以更改,但是靜態類型無法更改。

關于對象的靜態類型和動態類型,看一個示例:

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++如何防止一個類被其他類繼承

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/385450.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/385450.shtml
英文地址,請注明出處:http://en.pswp.cn/news/385450.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

linux里source、. 、sh、bash、./有什么區別

轉載&#xff1a;https://www.cnblogs.com/pcat/p/5467188.html 1.source a.sh source可以簡寫為“.”&#xff0c;即. a.sh 注意中間有空格&#xff0c;在當前shell內去讀取、執行a.sh&#xff0c;而a.sh不需要有"執行權限"。 2.sh a.sh 和 bash a.sh 都是打開…

【C++ Priemr | 15】虛函數表剖析(三)

一、虛擬菱形繼承 #include <iostream> using namespace std;class B { public:int _b; };class C1 :virtual public B { public:int _c1; };class C2 :virtual public B { public:int _c2; };class D :public C1, public C2 { public:int _d; };int main() {cout <&…

gcc的警告提示信息

gcc包含完整的出錯檢查和警告提示功能。采用-pedantic選項&#xff0c;對于不符合ANSI/ISO標準的源代碼會產生相應的警告信息。如&#xff1a;gcc -pedantic hello.c -o hello (main函數返回類型為int&#xff0c;且函數體內要有return 語句&#xff0c;一般為 return 0;) -pe…

1037. 在霍格沃茨找零錢(20)

如果你是哈利波特迷&#xff0c;你會知道魔法世界有它自己的貨幣系統 —— 就如海格告訴哈利的&#xff1a;“十七個銀西可(Sickle)兌一個加隆(Galleon)&#xff0c;二十九個納特(Knut)兌一個西可&#xff0c;很容易。”現在&#xff0c;給定哈利應付的價錢P和他實付的錢A&…

【Leetcode | 6】136. 只出現一次的數字

給定一個非空整數數組&#xff0c;除了某個元素只出現一次以外&#xff0c;其余每個元素均出現兩次。找出那個只出現了一次的元素。 說明&#xff1a; 你的算法應該具有線性時間復雜度。 你可以不使用額外空間來實現嗎&#xff1f; 示例 1: 輸入: [2,2,1] 輸出: 1 示例 2: 輸入…

gcc的優化功能

代碼優化的目的是改善程序的執行性能。gcc提供的代碼優化功能非常強大&#xff0c;它通過參數-On來控制優化代碼的生成&#xff0c;其中n為優化級別的整數&#xff0c;比較典型的范圍是從0變化到2或3&#xff08;與版本有關&#xff09;。 編譯時通過使用選項-O可以告訴gcc同時…

gcc編譯多個源代碼文件的過程(引出makefile)

由foo1.c foo2.c foo3.c 3個源文件組成的源程序生成最終的可執行程序foo的命令&#xff1a; gcc foo1.c foo2.c foo3.c -o foo 如果處理的源文件不止一個&#xff0c;則gcc會依次對每個文件進行預處理、編譯、匯編&#xff0c;最后將所有的目標代碼和庫文件進行&#xff0c;鏈…

觀擦者模式

/********************************************************************created: 2006/07/20filename: Observer.hauthor: 李創http://www.cppblog.com/converse/purpose: Observer模式的演示代碼 *********************************************************************/…

程序的裝入和鏈接

注&#xff1a;這是本人學習湯小丹等編寫的計算機操作系統&#xff08;西安電子科技大學出版社&#xff09;的學習筆記&#xff0c;因此許多引用來源于此書&#xff0c;在正文中就不注明了&#xff01; 程序在運行前需要經過以下步驟&#xff1a;編譯程序對源程序進行編譯生成…

內存對齊

1. 對齊原則&#xff1a; 數據成員對齊規則&#xff1a;結構(struct)(或聯合(union))的數據成員&#xff0c;第一個數據成員放在offset為0的地方&#xff0c;以后每個數據成員的對齊按照#pragma pack指定的數值和這個數據成員自身長度中&#xff0c;比較小的那個進行。結構(或…

1006. 換個格式輸出整數 (15)

讓我們用字母B來表示“百”、字母S表示“十”&#xff0c;用“12...n”來表示個位數字n&#xff08;<10&#xff09;&#xff0c;換個格式來輸出任一個不超過3位的正整數。例如234應該被輸出為BBSSS1234&#xff0c;因為它有2個“百”、3個“十”、以及個位的4。 輸入格式&a…

靜態庫的制作和使用

Linux下的靜態庫為lib*.a格式的二進制文件&#xff08;目標文件&#xff09;&#xff0c;對應于Windows下的.lib格式的文件。 &#xff08;1&#xff09;命名規則 lib庫名字 .a libMytest.a &#xff0c;則庫名字為mytest。下面以具體的代碼為例介紹如何制作靜態庫。 //mai…

IO多路復用之select

int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); 分析&#xff1a; nfds: 監控的文件描述符集里最大文件描述符加1&#xff0c;因為此參數會告訴內核檢測前多少個文件描述符的狀態 readfds&#xff1a; …

1031. 查驗身份證(15)

一個合法的身份證號碼由17位地區、日期編號和順序編號加1位校驗碼組成。校驗碼的計算規則如下&#xff1a; 首先對前17位數字加權求和&#xff0c;權重分配為&#xff1a;{7&#xff0c;9&#xff0c;10&#xff0c;5&#xff0c;8&#xff0c;4&#xff0c;2&#xff0c;1&…

虛擬地址空間

對于每一個進程都會對應一個虛擬地址空間&#xff0c;對于32位的操作系統&#xff08;其指令的位數最大為32位&#xff0c;因此地址碼最多32位&#xff09;&#xff0c;虛擬地址空間的大小為B即0~4GB的虛擬地址空間&#xff0c;其中內核空間為1GB&#xff0c;如下所示&#xff…

Leecode 69. x 的平方根

實現 int sqrt(int x) 函數。 計算并返回 x 的平方根&#xff0c;其中 x 是非負整數。 由于返回類型是整數&#xff0c;結果只保留整數的部分&#xff0c;小數部分將被舍去。 示例 1: 輸入: 4 輸出: 2 示例 2: 輸入: 8 輸出: 2 說明: 8 的平方根是 2.82842..., 由于返回類…

1002. 寫出這個數 (20)

讀入一個自然數n&#xff0c;計算其各位數字之和&#xff0c;用漢語拼音寫出和的每一位數字。 輸入格式&#xff1a;每個測試輸入包含1個測試用例&#xff0c;即給出自然數n的值。這里保證n小于10100。 輸出格式&#xff1a;在一行內輸出n的各位數字之和的每一位&#xff0c;拼…

C/C++中NULL指針

先談一下C/C的強制類型轉換Type cast。與強制類型轉換相對應的是自動類型轉換。或者強制類型轉換叫顯示類型轉換&#xff0c;自動類型轉換叫隱式類型轉換。自動類型轉換會在賦值運算、混合運算、參數傳遞、返回函數返回值、格式化輸出時且當類型出現不一致時發生&#xff0c;轉…

1009. 說反話 (20)

給定一句英語&#xff0c;要求你編寫程序&#xff0c;將句中所有單詞的順序顛倒輸出。 輸入格式&#xff1a;測試輸入包含一個測試用例&#xff0c;在一行內給出總長度不超過80的字符串。字符串由若干單詞和若干空格組成&#xff0c;其中單詞是由英文字母&#xff08;大小寫有區…

動態庫(共享庫)的制作和使用

Linux下的動態庫為lib*.so格式的二進制文件&#xff08;目標文件&#xff09;&#xff0c;對應于Windows下的.dll格式的文件。 &#xff08;1&#xff09;命名規則 lib庫名.so &#xff08;2&#xff09;動態庫的制作 1&#xff09;生成與位置無關的代碼&#xff08;.o&…