使用模板调用正确的派生类'方法而不使用虚拟?

时间:2012-04-26 18:23:12

标签: c++ performance templates inheritance

我有类似附件的东西。我基本上有一个Doer类,我想从它的成员调用Func()而不使用虚拟或尽可能少的代码重复。此外,提升也不是一种选择。我知道这个例子可能不那么清楚,但我希望你能得到这个想法。乙

class Base { // a bunch of shared base functionality. Cannot be instantiated by itself  }

class D1 : public Base
{
   void Func();
}

class D2 : public Base
{
   void Func();
}

//----

class Doer
{
   Doer(Base* b) : base(b) { } 

   void DoIt()
   {
      base->Func();
   }

   Base* base;
}

4 个答案:

答案 0 :(得分:3)

好吧,你可以Doer模仿:

template<class T>
class Doer
{
public:
   Doer(T* b) : base(b) { } 

   void DoIt()
   {
      base->Func();
   }

private:
   T* base;
};

但为此,我只需将virtual void Func()添加到Base

请注意,您可能希望在任何一种情况下公开Func: - )

答案 1 :(得分:0)

这种方法怎么样:

class Base { // a bunch of shared base functionality. Cannot be instantiated by itself  
   ~Base() { //stuff }
   void Func();
}

class D1 : public Base
{
   void Func();
}

class D2 : public Base
{
   void Func();
}

//----

class Doer
{
   Doer(Base* b) : base(b) { } 

   void DoIt()
   {
      base->Func();
   }

   Base* base;
}

由于Func()不是虚拟的并且被子节点重载,因此不应该存在vtable或任何引起的性能损失吗?

此外,需要在基类上调用析构函数,但声明它是虚拟的会强加一个vtable吗?

任何人都可以澄清吗?

由于

答案 2 :(得分:0)

你可以使用mixins!它们有利于优化(大量的内联机会,没有虚拟方法调用),但有时候有点难以推理。以下是使用mixins实现的示例:

template<class Base> class Doer : Base {
public:
    Doer() {}
    void DoIt() {
        this->Func();
    }
};

class D1 {
public:
    void Func() {
        cout<<"Hello from D1"<<endl;
    }
};

class D2 {
public:
    void Func() {
        cout<<"Hello from D2"<<endl;
    }
};

使用它有点不同,因为Doer与您的Base类实例相同。以下程序:

Doer<D1> *d1 = new Doer<D1>();
Doer<D2> *d2 = new Doer<D2>();
d1->DoIt();
d2->DoIt();

产生输出:

  

你好D1

     

来自D2的你好

这有一个明显的缺点,即D1和D2不会被强制实现“Func”方法。如果你忘了它,你将得到一个非常方便的C ++模板实例化错误,而不是“找不到方法”。如果您经常使用模板,Clang是一个很好的选择,因为您获得的编译器错误比g ++更有用。另一个缺点是构造函数:Doer定义默认构造函数,但不公开D1的构造函数。 C ++ 11允许构造函数继承,因此可以使用编译器标志来避免此问题。

答案 3 :(得分:0)

实际上你不需要参数化整个Doer类。这样可以正常工作(接近ccurtsinger的建议):

class Base {
public:
    void Func() {};
};

class B1 {
public:
    void Func() { cout << "in B1::Func" << endl;}
};

class B2 {
public:
    void Func() { cout << "in B2::Func" << endl;}
};

class Doer {
public:
    template <class B> void Do(B *pb) {pb->Func();}
};

int main() {
    B1 b1;
    B2 b2;
    Doer d;
    d.Do<B1>(&b1);
    d.Do<B2>(&b2);

    return 0;
}

但实际上还有一个更大的问题:从您最后使用的代码中看来,似乎在编译时您确切知道您正在处理哪些派生类对象,因此代码如下:

for(auto i = begin(B1_container); i != end(B1_container); ++i) {
    i->Func();
}
for(auto j = begin(B2_container); j != end(B2_container); ++j) {
    j->Func();
}

应该这样做。

我所说的是 - 你要么事先知道你在这里使用B1-s而在那里使用B2-s而且Func()调用没有额外的费用,或者你不知道你是哪一个是要处理下一个,然后你需要检查它的某种类型的特征或其他什么的动态类型,这是一个'如果'因此分支,从而错误预测和开销。请注意,我没有添加函数调用的成本,无论如何都存在。