簡體   English   中英

從模板派生類訪問方法而無需在c ++中使用虛函數?

[英]Accessing a method from a templated derived class without using virtual functions in c++?

我該如何解決? 我顯然不能使value()方法虛擬化,因為我事先不知道它是什么類型,並且從b訪問該方法時可能不知道這一點:

class Base
{
public:
    Base() { }
    virtual ~Base() { }
private:
    int m_anotherVariable;
};

template <typename T>
class Derived : public Base
{
public:
    Derived(T value) : m_value(value) { }
    ~Derived() { }

    T value() { return m_value; }
    void setValue(T value) { m_value = value; }
private:
    T m_value;
};

int main()
{
    Base* b = new Derived<int>(5);

    int v = b->value();

    return 0;
}

編譯錯誤:

error: 'class Base' has no member named 'value'

這個說法:

int v = b->value();

變量'b'被當作Derived <int>的對象進行處理。
因此,告訴編譯器:

int v = dynamic_cast<Derived<int>*>(b)->value();

注意:如果b不是Derived <int>,則強制轉換的結果為NULL。
因此,以下內容可能會更安全:

Derived<int>*  d = dynamic_cast<Derived<int>*>(b);
if (d)
{
    int v = d->value();
}
else
{
    // Error
}

或者,通過使用引用,您會拋出bad_cast異常:

// Throw bad_cast on failure.
Derived<int>& d = dynamic_cast<Derived<int>&>(*b);
int v = d->value();

或者,要變得晦澀難懂,我們可以一行完成。

// Assign v or throw bad_cast exception:
int v = dynamic_cast<Derived<int>&>(*b).value();

但是我認為您可以通過boost :: any實現您想做的事情

int main()
{
    boost::any   b(5);

    int    v = boost::any_cast<int>(b);

    b        = 5.6; // double
    double d = boost::any_cast<double>(b);
}

我認為,如果您問這個問題(根據我的經驗和我的設計),您的設計可能會有一些問題。
但是,有一些解決方法:

  1. 正如其他人已經回答的那樣,您可以將Base設為類模板。
  2. 您可以垂頭喪氣b(盡管我更願意避免垂頭喪氣)
  3. 您可以在Base中將value()聲明為返回boost :: any並將其設為純虛擬
  4. 您甚至可以將其聲明為返回void *(但不要這樣做。這是一個選擇,但很糟糕)

但是,這里真正的問題是您試圖在這里描述什么? Base,Derived和value()的含義是什么。 問自己這些問題,您可能不需要這些答案...

一些解決方案:

template < typename T>
class Base{
public: 
    Base() { }    
    virtual ~Base() { }
    virtual T value() = 0;
private:    
    int m_anotherVariable;
};

template <typename T>
class Derived : public Base<T> {
  ...
}

int main(){    
    Base<int>* b = new Derived<int>(5);    
    int v = b->value();    
    return 0;
}

另一個解決方案:

class Base {
public: 
    Base() { }    
    virtual ~Base() { }
    template<class T> T value() const;
private:    
    int m_anotherVariable;
};

template <typename T>
class Base2 : public Base {
public: 
    Base2() { }    
    virtual ~Base2() { }
    virtual T getValue() const = 0;    
};

template<class T> T Base::value() const {
    const Base2<T> * d = dynamic_cast<const Base2<T> *>(this);
    return d ? d->getvalue() : T();
}

template <typename T>
class Derived : public Base2<T> {
public:    
    Derived(T value) : m_value(value) { }    
    virtual ~Derived() { }    
    void setValue(T value) { m_value = value; }
    virtual T getValue() const { return  m_value; }    
private:    
     T m_value;
}

int main(){    
    Base* b = new Derived<int>(5);    
    int v = b->value<int>();
    return 0;
}

在這種情況下,Base需要了解模板類型,因此b-> value返回T。

我建議將模板添加到Base,然后在Base上使值成為虛擬函數

您可以將指針投射到Derived

int v = dynamic_cast<Derived<int>*>(b)->value();

當然,在實際代碼中,您必須添加檢查以確保dynamic_cast不會失敗。

為什么也不要使基於Base模板的類呢? 然后,您可以作為虛擬成員而有價值。

或者,您可以將b降為Derived。

看起來您想要動態多態性,但僅使用模板的“靜態多態性”。

如果要動態多態性,則確實需要基類中的虛擬成員(以及虛擬析構函數),或者如果沒有通用接口,則需要down_cast。

如果沒有,請刪除虛擬析構函數,並僅使用指向派生類型的指針或實例。

關於Base作為模板:

它會阻止您為動態多態性使用單個基類,因為Base <int>與Base <other>是不兼容的。

處理此問題的一種方法是通過訪客模式 基本思想是,在類的層次結構中,您將實現一個接受訪問者的onNode函數。 然后,您編寫特定的訪問者來執行您想要的操作。 就您而言,您將得到:

class GetIntValue : public BaseVisitor
{
public:
  GetIntValue (int & result)
  : m_result (result) {}

  void onNode (Derived<int> & d)
  {
    m_result = d.m_value;
  }
  void onNode (Derived<A> & d)
  {
    assert (! "calling GetIntValue on a node that doesn't have a value");
  }


private:
  int & m_result;
};

int main()
{
  Base* b = new Derived<int>(5);

  int v;
  GetIntValue(v).visit (*b);

  return 0;
}

暫無
暫無

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

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