簡體   English   中英

在C ++中繼承類的情況下強制進行后期方法解析

[英]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_class1bar_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM