簡體   English   中英

C++ 在模板上實現純虛擬 function

[英]C++ Implementing a pure virtual function over a template

我正在嘗試在模板(TElementHolder)上實現抽象 class(ElementHolder)。 function virtual T* Fun2()似乎工作正常,但 function virtual void Fun(T* element); 創建以下編譯錯誤:

source>:60:23: error: cannot declare variable 'fireHolder' to be of abstract type 'FireElementHolder'
   60 |     FireElementHolder fireHolder;
      |                       ^~~~~~~~~~
<source>:51:7: note:   because the following virtual functions are pure within 'FireElementHolder':
   51 | class FireElementHolder : public TElementHolder<Fire>
      |       ^~~~~~~~~~~~~~~~~
<source>:22:22: note:     'virtual void ElementHolder::Fun(Element*)'
   22 |         virtual void Fun(Element* element) = 0;
      | 

有人可以向我解釋為什么這兩個函數會產生不同的結果嗎? 如果我能以某種方式完成這項工作?

我不確定在模板上實現純虛擬 function是否是正確的說法。

來源: https://godbolt.org/z/qEYdrYfYa

#include <iostream>

//------------------------------------------------------------------------------------------
class Element
{
    public:
        virtual ~Element() {}
        virtual void Fun() = 0;
};
class Fire : public Element
{
    public:
        ~Fire() {}
        void Fun() { std::cout << "Fire" << std::endl; }
};
//------------------------------------------------------------------------------------------
class ElementHolder
{
    public:
        virtual ~ElementHolder() {}

        virtual void Fun(Element* element) = 0;
        virtual Element* Fun2() = 0;
};

template<class T>
class TElementHolder : public ElementHolder
{
    public:
        virtual ~TElementHolder();

        virtual void Fun(T* element);
        virtual T* Fun2();
};
template<class T>
TElementHolder<T>::~TElementHolder() {}

template<class T>
void TElementHolder<T>::Fun(T* element)
{
    element->Fun();
}

template<class T>
T* TElementHolder<T>::Fun2()
{    
    std::cout << "Hello Fun2" << std::endl;
    return new T();
}

class FireElementHolder : public TElementHolder<Fire>
{

};
//------------------------------------------------------------------------------------------


int main()
{
    FireElementHolder fireHolder;
    auto instance = fireHolder.Fun2();
    fireHolder.Fun(instance);
    instance->Fun();
}

這是工作代碼:

#include <iostream>
//------------------------------------------------------------------------------------------
class Element {
    public:
        virtual ~Element() = default;
        virtual void Fun() = 0;
};

class Fire: public Element {
    public:
        ~Fire() = default;
        void Fun() { std::cout << "Fire" << std::endl; }
};
//------------------------------------------------------------------------------------------
class ElementHolder {
    public:
        virtual ~ElementHolder() = default;
        virtual void Fun(Element* element) = 0;
        virtual Element* Fun2() = 0;
};

template<class T>
class TElementHolder: public ElementHolder {
    public:
        virtual ~TElementHolder() = default;
        virtual void Fun(Element* element) {
            element->Fun();
        }
        virtual T* Fun2() override {
            std::cout << "Hello Fun2" << std::endl;
            return new T();
        }
};

class FireElementHolder: public TElementHolder<Fire> {};
//------------------------------------------------------------------------------------------
int main() {
    FireElementHolder fireHolder;
    auto instance = fireHolder.Fun2();
    fireHolder.Fun(instance);
    instance->Fun();
}

一些亮點:

  1. 問題在於簽名

virtual void Fun(T* 元素)

必須更改為

虛空樂趣(元素*元素)

  1. 您在某些插入的地方缺少一些虛擬。

您的設計存在一個眾所周知的問題。

您有兩個並行的 class 層次結構ElementElelementHolder 您希望每個具體的ElementHolder接受指向相應Element的指針或引用。 因此,您將純虛擬 function 添加到基礎 class

class ElementHolder {
   virtual void Fun(Element* element) = 0;

並在每個派生的 class 中覆蓋它(為了便於說明,我對您的層次結構進行了模板化,您的代碼中有一個T而不是Fire ,但它並沒有真正影響任何東西):

class FireHolder : public ElementHolder {
   virtual void Fun(Fire* fire) { ... }

這是一個非常自然和明智的選擇。

除了它根本不起作用,因為void Fun(Fire* fire)不會覆蓋void Fun(Element* element) 它們是完全獨立的函數。

現在你如何解決這個問題? 有兩種非常不同的方式。

  1. 完全擺脫ElementHolder::Fun
  2. void Fun(Fire* fire)更改為void Fun(Element* fire) fireFire*里面。

沒有ElementHolder::Fun怎么辦? 簡單地說,就是不要調用它;)無論如何你都不能安全地使用它。 簽名Fun(Element*)表示可以傳遞任何元素。 實際上,您只能將Fire*傳遞給FireHolder ,而任何其他元素都是錯誤的(因此簽名存在——不是一個好主意)。 如果您只有一個ElementHolder和一個Element * ,則無法保證您可以調用Fun

如果您絕對必須調用ElementHolder::Fun ,那么 #2 是您唯一的選擇。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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