網站首頁 編程語言 正文
前言
C++中當子類覆寫(override)父類虛函數時,子類函數的返回值類型可以和父類函數的返回值類型不一致嗎?
先說結論:可以,但當且僅當它們的返回值類型是協變返回值類型(Covariant)時可以。C++中gcc從3.4開始支持這一特性。
什么是協變返回值類型(Covariant)
函數的協變返回值類型指的是子類中的成員函數的返回值類型不必嚴格等同與該函數所重寫的父類中的函數的返回值類型,而可以是更 “狹窄” 的類型。C++/Java等面向對象編程語言均支持協變返回值類型。
例子:
class Shape {
public:
virtual ~Shape() { }
virtual Shape* clone() const = 0; // Uses the copy constructor
virtual Shape* create() const = 0; // Uses the default constructor
};
class Circle : public Shape {
public:
Circle* clone() const; // Covariant Return Types; see below
Circle* create() const; // Covariant Return Types; see below
};
Circle* Circle::clone() const { return new Circle(*this); }
Circle* Circle::create() const { return new Circle(); }
C++中不支持virtual constructor,因為:
- 創建對象時需要知道對象的完整信息
- 虛函數機制也決定了對象尚未創建時,類的virtual table或許還不存在
- 我們不可能有指向virtual constructor的指針
但是我們可以通過上面的代碼實現類似的想法,如果我們擁有指向對象的指針:
- 通過clone()調用對象的拷貝構造函數,復制當前的對象
- 通過create()調用默認構造函數,創建新的對象
比如下面的使用場景:
void userCode(Shape* s)
{
Shape* s2 = s->clone();
Shape* s3 = s->create();
// ...
delete s2;
delete s3;
}
如果指針指向的是基類對象,調用上述函數時返回的就是指向基類對象的指針并賦值給s2/s3,如果指針指向的是子類對象,調用上述函數時返回的就是指向子類對象的指針并賦值給s2/s3。
協變返回值類型(Covariant)的作用
協變返回類型到底有什么用呢,編譯器為什么要支持這種語法?如果編譯器不支持,上面的例子將只能寫成如下這樣:
class Shape {
public:
virtual ~Shape() { }
virtual Shape* clone() const = 0; // Uses the copy constructor
virtual Shape* create() const = 0; // Uses the default constructor
};
class Circle : public Shape {
public:
Shape* clone() const; // Covariant Return Types; see below
Shape* create() const; // Covariant Return Types; see below
};
Shape* Circle::clone() const { return new Circle(*this); }
Shape* Circle::create() const { return new Circle(); }
這樣上面的userCode函數將不能通過編譯,上面調用clone函數部分將不得不改寫成下面這樣:
void userCode(Shape* s)
{
Shape* s2 = s->clone();
Circle* c = dynamic_cast<Circle*>(s2);
if (c != NULL) {
// c point to Circle
} else {
if (s2 != NULL) {
// s2 point to base Shape
}
}
}
// ...
delete s2;
}
通過if/else分支來判斷s是指向子類Circle還是指向基類Shape,失去了動態綁定的意義。
原文鏈接:https://blog.csdn.net/ithiker/article/details/109013385
相關推薦
- 2022-07-03 kali下對Docker的詳細安裝教程_docker
- 2022-07-13 Python字符串中如何去除數字之間的逗號_python
- 2021-12-14 linux下多線程中的fork介紹_Linux
- 2022-05-19 yolov5返回坐標的方法實例_python
- 2022-05-26 Python學習之文件的讀取詳解_python
- 2022-08-11 淺析k8s中各組件和kube?apiserver通信時的認證和鑒權問題_云其它
- 2022-04-14 遇到一個git的大坑 src refspec master does not match any e
- 2023-03-20 C#中如何限制TextBox控件內輸入值的范圍_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支