繁体   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