虚拟继承和静态继承 - 在C ++中混合

时间:2010-11-19 00:45:04

标签: c++ oop inheritance

如果你有这样的事情:

#include <iostream>

template<typename T> class A
{
public:
    void func()
    {
        T::func();
    }
};

class B : public A<B>
{
public:
    virtual void func()
    {
        std::cout << "into func";
    }
};

class C : public B
{
};

int main()
{
  C c;
  c.func();

  return 0;
}

是否动态调度func()? 你怎么能实现A类,如果B有一个虚拟覆盖,它是动态调度的,但如果B没有,则静态调度?

编辑:我的代码没编译?对不起大家。我现在病得很厉害。我的新代码也没有编译,但这是问题的一部分。此外,这个问题适合我,而不是常见问题。

#include <iostream>

template<typename T> class A
{
public:
    void func()
    {
        T::func();
    }
};

class B : public A<B>
{
public:
    virtual void func()
    {
        std::cout << "in B::func()\n";
    }
};

class C : public B
{
public:
    virtual void func() {
        std::cout << "in C::func()\n";
    }
};
class D : public A<D> {
    void func() {
        std::cout << "in D::func()\n";
    }
};
class E : public D {
    void func() {
        std::cout << "in E::func()\n";
    }
};

int main()
{
  C c;
  c.func();
  A<B>& ref = c;
  ref.func(); // Invokes dynamic lookup, as B declared itself virtual
  A<D>* ptr = new E;
  ptr->func(); // Calls D::func statically as D did not declare itself virtual
  std::cin.get();

  return 0;
}

visual studio 2010\projects\temp\temp\main.cpp(8): error C2352: 'B::func' : illegal call of non-static member function
      visual studio 2010\projects\temp\temp\main.cpp(15) : see declaration of 'B::func'
      visual studio 2010\projects\temp\temp\main.cpp(7) : while compiling class template member function 'void A<T>::func(void)'
      with
      [
          T=B
      ]
      visual studio 2010\projects\temp\temp\main.cpp(13) : see reference to class template instantiation 'A<T>' being compiled
      with
      [
          T=B
      ]

6 个答案:

答案 0 :(得分:2)

我不确定我明白你在问什么,但看起来你错过了必不可少的CRTP演员:

template<class T>
struct A {
  void func() {
    T& self = *static_cast<T*>(this);  // CRTP cast
    self.func();
  }
};

struct V : A<V> {  // B for the case of virtual func
  virtual void func() {
    std::cout << "V::func\n";
  }
};

struct NV : A<NV> {  // B for the case of non-virtual func
  void func() {
    std::cout << "NV::func\n";
  }
};

如果T没有声明自己的func,这将是无限递归,因为self.func将找到A&lt; T&gt; :: func。即使派生的T类(例如下面的DV)声明它自己的func但T没有声明它也是如此。

使用不同的最终覆盖物进行测试,以显示宣传的调度工作:

struct DV : V {
  virtual void func() {
    std::cout << "DV::func\n";
  }
};
struct DNV : NV {
  void func() {
    std::cout << "DNV::func\n";
  }
};

template<class B>
void call(A<B>& a) {
  a.func();  // always calls A<T>::func
}

int main() {
  DV dv;
  call(dv);   // uses virtual dispatch, finds DV::func
  DNV dnv;
  call(dnv);  // no virtual dispatch, finds NV::func

  return 0;
}

答案 1 :(得分:1)

  

如何实现A类,如果B有虚拟覆盖,则动态调度,但如果B没有,则静态调度?

有点矛盾,不是吗? A类用户可能对B或C一无所知。如果您有A的引用,了解func()是否需要动态调度的唯一方法是查阅vtable。由于A::func()不是虚拟的,因此没有条目,因此无处放置信息。一旦你虚拟化,你就会查看vtable并进行动态调度。

获取直接函数调用(或内联)的唯一方法是使用非虚函数,而不是通过基类指针间接传递。

编辑:我认为Scala中的这个成语是class C: public B, public A<C>(用子类重复特征)但这在C ++中不起作用,因为它使A<T>的成员在C中不明确{1}}。

答案 2 :(得分:1)

在您的特定示例中,不需要动态分派,因为c的类型在编译时是已知的。对B::func的调用将被硬编码。

如果您通过func呼叫B*,那么您将调用虚拟功能。但是在你非常人为的例子中,那将再次让你B::func

A*谈论动态调度没有多大意义,因为A是模板类 - 你不能制作通用的A,只有一个是绑定到特定的子类。

答案 3 :(得分:1)

  

如何实现A类,如果B有虚拟覆盖,则动态调度,但如果B没有,则静态调度?

正如其他人所注意到的那样,很难理解这个问题,但它让我记住了很久以前我学到的东西,所以这里有很长的回答你的问题:

template<typename Base> class A : private Base
{
public:
    void func()
    {
        std::count << "A::func";
    }
};

鉴于此,它取决于A的基础func()是否为虚拟。如果Base声明virtual,那么它也会在A中变为虚拟。否则它不会。见:

class V
{
public:
    virtual void func() {}
};
class NV
{
};

class B : public A<V>  // makes func() virtual
{
public:
    void func()
    {
        std::count << "B::func";
    }
};

class C : public A<NV>  // makes func() non-virtual
{
public:
    void func()
    {
        std::count << "C::func";
    }
};

这会碰巧回答你的问题吗?

答案 4 :(得分:0)

是否动态调度函数取决于两件事:

a)对象表达式是引用还是指针类型

b)函数(重载决议解析为)是否为虚拟。

现在来到你的代码:

  C c; 
  c.func();   // object expression is not of pointer/reference type. 
              // So static binding

  A <B> & ref = c; 
  ref.func(); // object expression is of reference type, but func is 
              // not virtual. So static binding


  A<D>* ptr = new D; 
  ptr->func(); // object expression is of pointer type, but func is not 
               // virtual. So static binding 

简而言之,'func'不会动态调度。

注意::抑制虚函数调用机制。

  

$ 10.3 / 12-“明确资格   范围运算符(5.1)抑制   虚拟的“呼叫机制。

OP2中的代码给出错误,因为只有当'Y'是'X'范围内的静态成员时,语法X :: Y才能用于在'X'范围内调用'Y'。

答案 5 :(得分:-1)

似乎你只需添加一些痕迹和用法来回答你自己的问题......

#include <iostream>

template<typename T> struct A { 
    void func() { 
        T::func(); 
    } 
}; 

struct B1 : A<B1> { 
    virtual void func() { 
        std::cout << "virtual void B1::func();\n";
    } 
}; 

struct B2 : A<B2> { 
    void func() { 
        std::cout << "void B2::func();\n";
    } 
}; 

struct C1 : B1 { }; 
struct C2 : B2 { }; 

struct C1a : B1 {
    virtual void func() {
        std::cout << "virtual void C1a::func();\n";
    }
};

struct C2a : B2 {
    virtual void func() {
        std::cout << "virtual void C2a::func();\n";
    }
};

int main()
{
    C1 c1; 
    c1.func(); 

    C2 c2; 
    c2.func(); 

    B1* p_B1 = new C1a;
    p_B1->func();

    B2* p_B2 = new C2a;
    p_B2->func();
}

输出:

virtual void B1::func();
void B2::func();
virtual void C1a::func();
void B2::func();

结论:A确实具有B的功能的虚拟性。