0

我有类似附件的东西。我基本上有一个 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

4 回答 4

3

好吧,您可以进行Doer模板化:

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

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

private:
   T* base;
};

但为此,我只想添加一个virtual void Func()to 来Base代替。

请注意,无论哪种情况,您都可能希望Func公开 :-)

于 2012-04-26T18:28:02.833 回答
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?

谁能澄清一下?

谢谢

于 2012-04-26T18:51:52.523 回答
0

你可以使用混合!它们有利于优化(很多内联机会并且没有虚拟方法调用),但有时有点难以推理。这是您使用 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 允许构造函数继承,因此可以使用编译器标志来避免这个问题。

于 2012-04-26T19:59:59.707 回答
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() 调用没有额外的成本,或者你不知道你要处理哪一个使用 next 然后您需要检查它是某种特征或其他类型的动态类型,这是一个“if”,因此会出现分支,从而导致错误预测和开销。请注意,我没有添加函数调用的成本,无论如何,这两种情况都存在。

于 2012-04-27T12:51:03.353 回答