[英]Forcing late method resolution in case of class inheritance in c++
考慮以下類結構:
class foo {
public:
int fun () {
cout << "in foo" << endl;
}
};
class bar_class1:public foo {
public:
int fun () {
cout << "in bar_class1" << endl;
}
};
class bar_class2:public foo {
public:
float fun () {
cout << "in bar_class2" << endl;
}
};
main () {
foo * foo_pointer = new bar_class1();
foo_pointer->fun();
}
上面程序的輸出在foo中 。 有沒有辦法使用foo *
類型的指針實際指向bar_class1
或bar_class2
類型的對象,我們可以調用派生類而不是基類的fun
函數? 由於無法在基類foo
中使該fun
函數虛擬化,因此,派生類bar_class2
中的foo
函數存在返回類型沖突。
這是我的評論作為答案。
你不能這樣做。
如果這種多態性是可能的,那么當代碼在實際類型為bar_class2並因此獲得浮點數的對象上調用foo::fun
(期望一個int)時,這不會令人震驚嗎? 您是否只想放棄類型安全性?
如果您想要不同的返回類型,聽起來就像您想要一個模板。 但是您不能完全按照想要使用foo()的方式使用模板。 靜態多態性(模板)和運行時多態性(后期綁定)不能很好地融合在一起。 您需要重新設計您的oop結構。
如果您非常厭惡型的安全,你可以排序與空指針做到這一點。 但是,對於飛行意大利面條怪獸的熱愛,永遠不要使用C ++做到這一點。 閱讀以下代碼之前,請先閉上眼睛,以免暴露。
#include <iostream>
class foo {
public:
virtual void* fun() = 0;
virtual ~foo(){};
};
class bar_class1: public foo {
public:
void* fun() {
return &value;
}
private:
int value = 1;
};
class bar_class2: public foo {
public:
void* fun() {
return &value;
}
private:
float value = 1.1;
};
int main() {
foo* foo_pointer1 = new bar_class1();
foo* foo_pointer2 = new bar_class2();
// in c++ compiler must know the type of all objects during compilation
std::cout << *reinterpret_cast<int*>(foo_pointer1->fun()) << '\n';
std::cout << *reinterpret_cast<float*>(foo_pointer2->fun()) << '\n';
delete foo_pointer1;
delete foo_pointer2;
}
也許與現有的答案類似,我真的希望您意識到更改設計要比這個爛攤子要好,但是我相信這是您將獲得的最好的選擇。 我強迫您在調用站點上指定返回類型(例如someFoo->fun<int>()
),因為您無論如何都必須知道它,然后根據該調度。 任何有趣的事情,都會有例外。 還要記住,我想這是不理想的。
#include <cassert>
#include <stdexcept>
#include <type_traits>
struct foo {
virtual ~foo() = default;
template<typename T, typename = typename std::enable_if<std::is_same<T, int>::value>::type, typename = void>
T fun();
template<typename T, typename = typename std::enable_if<std::is_same<T, float>::value>::type>
T fun();
};
struct bar_class1 : foo {
int fun() {
return 2;
}
};
struct bar_class2 : foo {
float fun() {
return 3.5f;
}
};
template<typename T, typename, typename Dummy>
T foo::fun() {
if (auto *p = dynamic_cast<bar_class1 *>(this)) {
return p->fun();
} else if (dynamic_cast<bar_class2 *>(this)) {
throw std::invalid_argument("Mismatching dynamic type.");
} else {
return 1;
}
}
template<typename T, typename>
T foo::fun() {
auto *p = dynamic_cast<bar_class2 *>(this);
if (dynamic_cast<bar_class1 *>(this) || !p) {
throw std::invalid_argument("Mismatching dynamic type.");
} else if (auto *p = dynamic_cast<bar_class2 *>(this)) {
return p->fun();
}
assert(false); //should never get here, but compiler doesn't know that
}
如果您需要主要功能,我已經寫了一個完整的示例 。
要回答您的問題: 不,如果不確定返回類型,則不可能進行后期綁定。 ...至少不是以合理的方式,請參閱user2079303的出色反例 。 但...
您可以使用關鍵字virtual
將您的代碼(例如)更改為以下內容,並將返回類型均等化為void
:
class foo
{
public:
virtual void fun(std::ostream& out) {
out << "in foo" << std::endl;
}
};
因此您可以稍后決定輸出類型:
class intFoo: public foo
{
public:
void fun(std::ostream& out) {
// output an int
out << "in bar_class1. data: " << data << endl;
}
int data;
};
class floatFoo: public foo
{
public:
void fun(std::ostream& out) {
// output a float
out << "in bar_class2. data: " << data << endl;
}
float data;
};
為簡便起見,我重復使用了輸出流-現在是fun()
函數的參數-此函數用於演示派生類的依賴於類型的部分。 在您的應用程序中,參數可能是另一個更有用的類型。
fun函數不是虛擬函數,因為您沒有使用關鍵字“ virtual”來裝飾它。 因此,編譯將確定在編譯時調用哪個函數。 因此,沒有辦法告訴編譯器調用另一個函數,因為編譯器將使用其靜態類型,即變量定義類型-foo *。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.