【问题标题】:Virtual inheritance use虚拟继承使用
【发布时间】:2013-12-18 03:17:47
【问题描述】:

我需要编写一个可供新手和有经验的 C++ 开发人员使用的编码约定。动态多态的继承规则是这样的:

  • 对于动态多态性,考虑使用单继承(树状层次结构),可能使用抽象接口的多继承
  • 对于沿层次结构(基类等)的继承,默认情况下,使用公共继承
  • 对于抽象接口的继承,默认,使用公共虚拟继承

此规则后面将提供有关实施、可能的例外情况等的详细信息。

那么,问题是:这个规则对新手和有经验的 C++ 开发人员都适用吗?(欢迎提供优点/缺点以及来源和链接)


我看到的是:

优点:

  • 规则易于新手使用,不限制有经验的开发人员。
  • 熟悉那些已经熟悉 Java/.NET 接口的人
  • 避开了与实现的虚拟继承相关的问题(因为它是为抽象接口保留的)以及非虚拟继承(转换为接口类时可能存在歧义)

缺点:

  • 轻微的性能成本(转换到接口时的速度、虚拟表的大小、类实例中的附加指针)

注意:我已阅读以下在线资源:

注 2:“抽象接口”名称的使用是在 Sutter 和 Alexandrescu 在“C++ 编码标准”第 36 条中使用之后创造的


这是一种应该可以工作的情况(使用接口的 Java/C# 等价物也可以工作),但如果接口继承不是虚拟的,则在 C++ 中就不行:

class A
{
   public :
      virtual ~A() = 0 {}
} ;

class B : public A {} ; // should have been virtual to avoid the error
class C : public A {} ; // should have been virtual to avoid the error

class D : public B, public C
{
   public : 
      virtual ~D() {}
} ;

void foo(A * c) {}
void bar(D * d)
{
   foo(d) ; // Error: ambiguous conversions from 'D *' to 'A *
}

是的,为了消除歧义而进行显式转换是错误的解决方案(无论如何,显式转换通常是错误的解决方案)。

【问题讨论】:

  • 对我来说似乎很好。我还要添加一条规则:“尽可能避免 OO 多态性。尝试使用 CRTP 和 ad-hoc polymorphism using type-erasure 等替代方法”
  • 我想说实现继承通常应该是私有的(仅在必要时公开成员),并且只有在有充分理由不喜欢组合时才这样做。接口继承通常应该是非虚拟的,因为派生类通常是最终的。一般来说,让继承尽可能简单(但不要更简单)。
  • @user3034482 :比较基类指针?做什么的?我从来没有在我的问题中提到过,是吗?无论如何,你还在为微优化而战吗?当您的结构为 500 字节或更大时,一个指针的性能损失是多少?一个虚函数调用在进行可能跨越 1 毫秒或更长时间的冗长排序时对性能有何影响?禁用 RTTI 或异常?拜托,我们是在 2013 年,而不是 1990 年......如果我想要性能,我会删除所有 qsort 调用并 void * 污染代码,然后用 std::sort 和仿函数替换。这不是重点。
  • @user3034482:please look at the Google C++ style guide:我做到了。事实上,StackOverflow 的很多 Top 1% 的人都这样做了,而他们的 cmets 对此并不十分积极。 “Google C++ Style reference”并不是真正的 C++ 参考,而更像是“如果您是新手,不想学习,请使用该子集,谢谢”。如果您需要 C++ 的参考资料,请使用严肃的文档,例如 Sutter 和 Alexandrescu 的 C++ 编码标准,或查看 F-35 “JSF C++ 编码标准”。
  • @user3034482:they don't do rtti and exceptions; its for good reasons。对于RTTI(规则是“避免”,没有提到效率,只有设计)。至于异常,这是基于 Google 的代码不是异常安全的事实做出的决定,这是一种礼貌的说法,它很容易泄漏内存和资源,因此比其他任何东西都更能证明代码的状态。同样,几乎没有提到效率。

标签: c++ oop inheritance multiple-inheritance


【解决方案1】:

你知道吗?您已经在问题中提供了所有重要信息。在技​​术层面上,我看不出有什么可以回答的。而且显然没有其他人发现您发布的内容有任何重大技术问题。

不过,我会回答你的粗体问题:是的,它适合新手和专业人士。

  • 新手有一些有用的技术指南。
  • 专业人士可以做他们想做的事如果他们可以给出理由,因为你用“考虑”和“默认”来限定你的规则,所以基本上没有其他人可以说你必须这样做或所以因为样式规则,因为你的规则的措辞已经允许例外。

【讨论】:

    【解决方案2】:

    您的第一条规则完全排除了class D:

    对于动态多态,考虑使用单继承(树状层次结构),可能使用抽象接口的多重继承

    你的规则很好。

    那么,您的问题是什么?现在还剩下什么?

    【讨论】:

      【解决方案3】:

      如果继承示例不是虚拟的,则由于 c++ 中的经典多重继承问题diamond of death,您的继承示例不起作用。基本上,如果您不指定虚拟继承,则每个父类(B,C)都有自己的基础 A 对象。这使得对非静态基类函数和变量(转换以及我假设)的所有访问都模棱两可。我无法想象在 C++ 中可以避免这种情况的情况。

      编辑:记录在案,这是工作代码:

      class A
      {
      public :
          virtual ~A() {}
      };
      
      class B : virtual public A {};
      class C : virtual public A {};
      
      class D : virtual public B, virtual public C
      {
      public :
          virtual ~D() {}
      };
      
      void foo(A * c) {}
      void bar(D * d)
      {
          foo(d);
      }
      
      int main(void)
      {
          D d;
          foo(&d);
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 2016-10-26
        • 2013-08-24
        • 2016-03-26
        • 2011-01-08
        相关资源
        最近更新 更多