[英]How to call to non-static parent function from within child function in Qt/C++?
[英]How to downcast in C++ to call a child function from a parent instance?
我試圖使用顯式向下轉換從父實例調用子函數(感謝指出@Aconcagua)。 作為C ++的初學者,我有這樣的事情:
Road currentRoad = ...;
duration = ((SpeedDataRoad) currentRoad).getSpeedProfileTime(dateinMillis, isRightDirection);
類SpeedDataRoad
繼承自Road
:
class SpeedDataRoad : public Road{
double getSpeedProfileTime(long dateinMillis, bool isRightDirection) {
...
}
但是我收到錯誤:
從'Road'到'SpeedDataRoad'的C風格演員陣容沒有匹配的轉換
任何關於我做錯的建議都將不勝感激。
為了清楚起見,我在Java中嘗試實現的內容將像這樣編寫並正常工作:
duration = ((SpeedDataRoad) currentRoad).getSpeedProfileTime(currentTime, isRightDirection);
您遭受稱為“對象切片”的效果:
SpeedDataRoad sdr;
Road currentRoad = sdr;
在第二行, sdr
按值分配給currentRoad
,但后者不適合保存完整的SpeedDataRoad
對象。 因此,所有剩余的SpeedDataRoad
都被簡單地切除,剩下的只是一個純粹的Road
對象,只包含原始sdr
對象的Road
部分。
同時,由於您只剩下一個純Road
對象,因此無法將其SpeedDataRoad
回SpeedDataRoad
對象。 現在缺少的部分應該從哪里來?
這與您不能將多態類型直接放入基類的容器(如std::vector
)中的原因完全相同。
你需要的是指針(如果你想能夠重新分配)或引用(否則是首選):
SpeedDataRoad sdr;
Road& currentRoad = sdr;
// ^ (!)
// or:
Road* currentRoad = &sdr;
現在你可以做演員了。 但明確的下線有一種糟糕的設計氣味。 從一開始就使用多態方法可能會更好:
class Road
{
public:
virtual double getSpeedProfileTime(long, bool) = 0;
// ^ pure virtual
// alternatively, you can provide a default implementation
};
class SpeedDataRoad : public Road
{
public:
double getSpeedProfileTime(long, bool) override
{ /* ... */ }
};
現在你可以簡單地:
SpeedDataRoad sdr;
Road& currentRoad = sdr;
double profile = currentRoad.getSpeedProfileTime(0, false);
作為虛擬,你總是得到函數的正確變體,無論我們擁有哪個子類,以及它可能覆蓋函數的方式......
旁注1:您可能更喜歡更現代的C ++演員表,而不是舊的C風格演員陣容,您可以控制更精細的實際操作:
Road* someRoad = ...;
SpeedDataRoad* sdr = static_cast<SpeedDataRoad*>(someRoad);
SpeedDataRoad* sdr = dynamic_cast<SpeedDataRoad*>(someRoad);
如果您100%確定對象只能是所需類型,則可以使用static_cast
。 在這種情況下,你可以避免任何根本不能提供任何服務的運行時測試(你還是100%肯定,還記得嗎?)。 奇怪的重復模板模式是一個典型的場景。
如果你不能確定類型,那么dynamic_cast
就會起作用,它會做一些運行時類型檢查,只返回一個空指針(如果在指針上使用)或者拋出一個std::bad_cast
(如果用在引用上),如果實際類型不是所需類型(或子類)。 當不同的多態類型存儲在向量中時(作為基類的指針,見上文),可能會出現這種情況。 但是再說一遍:需要一個演員可能暗示你的設計存在缺陷......
(為了完整性:還有const_cast
和reinterpret_cast
,但你應該遠離這些,除非你真的,真的知道你做了什么。)
附注2:與Java的差異。
在Java中,我們在本機和引用類型之間隱式區分。 原生的總是通過值傳遞,引用類型總是通過引用傳遞 - 好吧, Java引用 ,它實際上比C ++引用更像是一個C ++ 指針 (可以為null
,可以重新賦值)。 在Java中,這是隱式發生的,在C ++中,您需要明確(另一方面,您可以為任何類型都有兩種行為)。
Java強制轉換(Java!)引用的行為類似於C ++ dynamic_cast
(在引用時,即拋出,它在類型不匹配時不會返回null
)。
最后(關於我的多態性建議),在Java中,所有函數都是隱式虛擬的,在C ++中,你必須再次明確(應用virtual
關鍵字,見上文)。
您正在切割 SpeedDataRoad
對象。 與具有指針/引用語義的Java對象不同,C ++對象具有值語義。 這意味着,在您的示例中, currentRoad
是Road
,而不是SpeedDataRoad
。 它是您在...
創建SppedDataRoad
的Road
部分的副本 。
要使用多態性是C ++,您需要使用引用或指針。 也就是說,以下操作無效,因為currentRoad
不是 SpeedDataRoad
:
double foo(Road currentRoad)
{
//...
return ((SpeedDataRoad)currentRoad).getSpeedProfileTime(currentTime, isRightDirection);
}
int main()
{
SpeedDataRoad road;
foo(road);
}
雖然以下內容可行,因為currentRoad
引用了SpeedDataRoad
:
double foo(Road& currentRoad)
// ^---------------- Pass by reference now
{
//...
return dynamic_cast<SpeedDataRoad&>(currentRoad).getSpeedProfileTime(currentTime, isRightDirection);
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Using C++-style cast and casting reference to reference
}
int main()
{
SpeedDataRoad road;
foo(road);
}
在第一個示例中, currentRoad
是Road
road
的副本 ,而在第二個示例中, currentRoad
是對road
的引用 。
您還應該避免在C ++中使用C風格的強制轉換。 在這種情況下,最好使用dynamic_cast<SpeedDataRoad&>(currentRoad)
或者,如果您絕對確定currentRoad
將始終是對SpeedDataRoad
對象的引用, SpeedDataRoad
static_cast<SpeedDataRoad&>(currentRoad)
。 如果currentRoad
不是對SpeedDataRoad
的引用,前者將執行運行時類型檢查並拋出異常,而后者將避免執行運行時類型檢查的(小)開銷,但如果currentRoad
不是引用,則會導致未定義的行為到SpeedDataRoad
。
這被稱為向下傾斜而不是向上 - 通過dynamic_cast
直截了當地實現這一目標:
if (SpeedDataRoad* sdroad = dynamic_cast<SpeedDataRoad*>(¤tRoad); sdroad != nullptr) {
duration = sdroad->getSpeedProfileTime(currentTime, isRightDirection);
}
如果要在函數內部檢查是否可以將指針/引用向下轉換為子項,則需要使用動態強制轉換。
void foo(Road* road){
SpeedDataRoad* child{nullptr};
if(child = dynamic_cast<SpeedDaraRoad*>(road){
// Do something with SpeedDataRoad
} else {
// road is not an instance of SpeedDataRoad
}
}
你也可以使用帶引用的dynamic_cast
,就像這樣
cppSpeedDataRoad& child = dynamic_cast<SpeedDataRoad&>(reference_to_road);
但要小心,好像演員表失敗, std::bad_cast
將被拋出。
在C ++中,我們盡量不使用C風格的轉換。 (typename)object
。 相反,有4種類型的類型轉換。
static_cast<typename*>(pointer) static_cast<typename>(value)
:指針的上傳和值類型的類型轉換
dynamic_cast<typename*>(pointer)
: dynamic_cast<typename*>(pointer)
安全向下轉換(你應該使用的那個)。 它對upcasting進行運行時檢查,因此存在運行時成本。
const_cast<...>(...)
:常量類型
reinterpret_cast<typename*>(pointer) reinterpret_cast<typename>(value)
:類似於C風格的強制轉換。
對於對象(已分配堆棧),我很少使用強制轉換。 因為C ++中的自然對象有自己的大小,所以轉換可能會改變它們的大小。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.