【C++】動態(tài)多態(tài)-創(chuàng)新互聯(lián)

多態(tài)【Polymorphism】是面向對象程序設計的一個重要特性,即一個接口,多種實現(xiàn)方式。

專注于為中小企業(yè)提供成都做網(wǎng)站、成都網(wǎng)站制作服務,電腦端+手機端+微信端的三站合一,更高效的管理,為中小企業(yè)鳩江免費做網(wǎng)站提供優(yōu)質的服務。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動了近1000家企業(yè)的穩(wěn)健成長,幫助中小企業(yè)通過網(wǎng)站建設實現(xiàn)規(guī)模擴充和轉變。

在C++中,多態(tài)體現(xiàn)為兩個方面,一個是編譯時多態(tài),一個是運行時多態(tài)。編譯時多態(tài)是靜態(tài)多態(tài),運行時時動態(tài)多態(tài)。

靜態(tài)多態(tài)分為【函數(shù)重載】和【模板(也稱為泛型編程)】。

函數(shù)重載主要是C++ 將參數(shù)列表也作為函數(shù)的標識,不想C,只是將函數(shù)名作為標識。那么就存在最佳匹配問題。

運行時多態(tài)主要指【虛函數(shù)】

虛函數(shù):
在基類聲明一個虛函數(shù),在子類中進行重寫?;惖囊没蛘咧羔樖侵赶蚺缮惖膶ο蟮?,基類調用該函數(shù)時,自動指向派生類的函數(shù),這就是動態(tài)多態(tài)。

注意:如果不聲明一個虛函數(shù),那么就是【函數(shù)隱藏/覆蓋】,注意這里是子類和父類擁有同名函數(shù),而無需考慮參數(shù)列表。

具體實現(xiàn)
每個有虛函數(shù)的類都會有一個虛函數(shù)表,這里保存的就是虛函數(shù)的地址信息。所有存在虛函數(shù)的類的實例都會在內存中保存一個虛函數(shù)表,當調用虛函數(shù)的時候,從虛函數(shù)表中查找對應的需要調用的函數(shù)地址。

編譯器會為每個存在虛函數(shù)的類對象插入一個vtpr(virtul function pointer),該vptr指向存放了虛函數(shù)地址的虛函數(shù)表vtbl,這樣對象在調用虛函數(shù)的時候,第一步會先根據(jù)vptr找到vbtl,然后根據(jù)該虛函數(shù)在vbtl中的索引來進行調用,這樣就實現(xiàn)了運行時多態(tài)功能。

大部分編譯器的實現(xiàn),都是將vptr放在對象的首位,所以我們可以通過這個特點來直接調用虛函數(shù)表中的函數(shù)。

Derived d;
long address = *(long*)&d;
Fun fun= (Fun)(*(long*)address);

對比普通成員函數(shù)和虛函數(shù)
在C++中,允許對函數(shù)進行重載,那么就需要對函數(shù)進行name mangling。

在此,說下編譯器mangling后函數(shù)名的規(guī)則,仍然以成員函數(shù)Print()優(yōu)化后的名稱_ZN4Base5PrintEv為例(這個規(guī)則以筆者使用的gcc為例):

  • 編碼后的符號由_Z開頭
  • 如果有作用域符,則在_Z之后加上N
  • 接著是命名空間名字長度、命名空間名字、類名字長度、類名、成員函數(shù)名稱、函數(shù)名稱
  • 如果有作用域符,則以E結尾
  • 最后加上函數(shù)形參符號,void是v,int是i,char是c,P代表指針,有幾個形參就寫幾個符號

從上述規(guī)則我們可以看出,C++中的重載只跟函數(shù)名和函數(shù)參數(shù)有關。

而對于成員函數(shù),會將其轉化成對應的非成員函數(shù):
#1 安插一個額外的參數(shù),const class *this 進入成員函數(shù)
#2 將成員函數(shù)進行mangling 處理,轉換成獨一無二的函數(shù)名
最終會被轉化成一個普通的函數(shù)。

而虛函數(shù)對比普通的成員函數(shù),多了一步虛函數(shù)尋址。
不過基類指針指向什么具體類型,但是總可以找到對應對象的vtbl,就可以進行函數(shù)訪問。

C++17中引入了 variant 和 visit 以實現(xiàn)多態(tài)

variant是C++17引入的變體類型,它大的優(yōu)勢是提供了一種新的具有多態(tài)性的處理不同類型集合的方法。也就是說,它可以幫助我們處理不同類型的數(shù)據(jù),并且不需要公共基類和指針。

可以將其理解為union的升級版,之所以稱之為升級版,是因為union有如下缺點:

  • 對象并不知道它們現(xiàn)在持有的值的類型
  • 不能持有std::string等非平凡類型
  • 不能被繼承

既然稱之為union的升級版,那么union的缺點其肯定不存在的,在此我們整理了下variant的特點:

  • 可以獲取當前類型
  • 可以持有任何類型的值(不能是引用、C類型的數(shù)組指針、void等)
  • 可以被繼承

【visit】
定義:

templateconstexpr visit( Visitor&& vis, Variants&&... vars );

在上述定義中,vis是一個訪問器,而vars則是傳給訪問器的參數(shù)列表。換句話說,std::visit能將所有變體類型參數(shù)所存放的數(shù)據(jù)作為參數(shù)傳給函數(shù)。

std::visit訪問器可以是函數(shù)對象、泛型lambda以及重載的lambda等。

#include#include#includestruct Visitor {void operator()(int n) const {std::cout<< "int: "<< n<< std::endl;
  }
  
  void operator()(const std::string &str) const {std::cout<< "string: "<< str<< std::endl;
  }
};
  
int main() {std::variantv;
  Visitor vst;
  v = "with Visitor";
  std::visit(vst, v);
  return 0;
}

輸入如下:

string:with Visitor

結合variant 和 visit 實現(xiàn)多態(tài)

struct CallPrint {void operator()(const Base& b) {b.Print(); }    
    void operator()(const Derived& d) {d.Print(); }    
};

int main() {std::variantv = Derived();
  std::visit(CallPrint{}, v);
  v = Base();
  std::visit(CallPrint{}, v);
  return 0;
}

這就需要從其優(yōu)缺點來進行分析,使用者可以根據(jù)其特點進行選擇,首先,總結下其優(yōu)點:

  • 值語義,無需動態(tài)分配
  • 不需要基類,類之間可以不相關
  • 相比于虛函數(shù)的重載(函數(shù)名、參數(shù)完全一致),variant只需要函數(shù)名一致即可,即不同的類里面可以函數(shù)名相同而參數(shù)不同,通過visit來進行對應的調用,從而實現(xiàn)多態(tài)

看完了前面的內容,其缺點也相對來說比較明顯,如下:

  • 需要在編譯時預先了解所有類型
  • 浪費內存,因為std::variant大小是支持類型的大大小。因此,如果一種類型是 10 字節(jié),另一種是100 字節(jié),那么每個變體至少是 100 字節(jié)。因此,您可能會丟失 90 個字節(jié)
  • 每個多態(tài)操作都需要實現(xiàn)一個對應的visit

你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧

網(wǎng)站欄目:【C++】動態(tài)多態(tài)-創(chuàng)新互聯(lián)
地址分享:http://bm7419.com/article16/dihcgg.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供移動網(wǎng)站建設、企業(yè)建站、App設計、網(wǎng)頁設計公司、手機網(wǎng)站建設、網(wǎng)站收錄

廣告

聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經(jīng)允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)

綿陽服務器托管