【问题标题】:C++ struct inheritance [closed]C++结构继承[关闭]
【发布时间】:2017-09-21 08:14:50
【问题描述】:

据我所知,C++ 中的类和结构之间的主要区别(并且可能是唯一的区别)是,类的成员默认为私有,而结构的成员默认为公共。

但是,可能因为我之前是一名 C 开发人员,我仍然继续声明结构以仅保存“公共”字段,而且我几乎从不声明方法(初始化成员的构造函数除外)。

我还想利用结构的 C++ 继承。

我的问题是:

  1. 即使语言允许,继承结构是一种好习惯吗?
  2. 是否可以阻止结构声明一个虚拟方法,这将创建一个 vtable 并修改结构的大小?

谢谢

【问题讨论】:

  • @Caleth 只是有时。其他时候为虚拟会员支付开销是个坏主意。这一切都取决于。
  • @Caleth 有时。有时你只需要继承。并且没有虚拟析构函数。
  • C++ 中真的没有“结构”。您可能会问,继承使用 struct 关键字定义的类是否有意义,但这是见仁见智的问题。
  • @Caleth 模板方法模式,CRTP,扩展异常类型,通过使用私有继承节省大量输入,以及需要“is-a”关系而我们不 想要动态分配...
  • @juanchopanza 吹毛求疵:扩展(标准)异常类型并不是排除虚拟的继承示例,因为std::exception::~exception 虚拟的。

标签: c++ inheritance struct


【解决方案1】:

类的成员默认是私有的,结构的成员默认是公开的。

结构也默认继承公共,类默认继承私有。

Even if the language allows it, is it a good practice to inherits structs ?

当然。它完全按照您的预期工作。

是否可以阻止结构声明一个虚拟方法,这将创建一个 vtable 并修改结构的大小?

还没有。有一个关于 C++20+ (P0707) 的提议可以完全实现这一点,但它还很年轻,而且实现得还不够远,无法在任何地方使用。特别是,搜索“3.6 plain_struct”,看看它们是如何强制使用普通结构的。

一般来说,当您将结构用作“结构”类型的函数时,我建议您使用它——保存没有不变量的数据。如果你有不变量,你应该使用封装和数据隐藏来保持它们,所以它应该是一个类。

【讨论】:

  • 您的最后一段显然是基于观点的,可能会向新用户暗示struct 与 c++ 中的class 不同,但事实并非如此。
  • @Holt 对程序员的意义比对编译器的意义重要。比较 struct pair { T1 first; T2 second; ... }class map { /* no public data members */ ... }
  • @Caleth 但你的意见认为struct 就是你所说的意思。我并不是说这不是一个好意见,我只是说这不是所有程序员的一般规则,所以它不应该出现在 SO 答案中。
  • @Holt 这些例子来自namespace std。关于 good design 的建议是关于 SO 的主题
  • @Holt 我向用户推荐class 暗示不变量存在,struct 暗示它们不存在。这既是一种普遍接受的沟通方式,也是使您的代码对使用相同沟通方法的其他人更清晰的好建议。正如您正确指出的那样,除了默认访问说明符之外没有其他技术差异。
【解决方案2】:

只想解决这个问题:

即使语言允许,继承结构是一种好习惯吗?

你应该摆脱“struct”表示POD的内涵。有时,尽管有一些行为,但最可重用的组件是那些不封装任何东西的组件。

例如,考虑这个元函数:

template<typename T> struct is_foo :      std::false_type {};
template<>           struct is_foo<Foo> : std::true_type  {};

以上所有类型(以及 true 和 false 别名后面的类型)都使用 struct 关键字声明。这仅仅是因为默认情况下将所有内容都公开会转发我们想要的行为,而我们不必每次都将其拼写出来。

您发现自己继承自“结构”的另一次是在扩展 C 库时。如果库定义了一个名为struct Bar 的结构用于与其通信,那么向它添加功能的最简单方法是从Bar 继承。像这样:

class ExtendedBar : Bar {
  void mem_func() {
    //Need to call the C library function? No problem
    c_library_func(this); // ExtendedBar is-a Bar
  }
};

唯一重要的区别是默认的可访问性级别。您唯一应该关心的 (IMO) 是哪种默认可访问性最适合您的目的。

【讨论】:

  • 如果模板使用有专长,我会非常高兴。 template&lt;&gt; using is_foo&lt;Foo&gt; = std::true_type;
【解决方案3】:

即使语言允许,继承结构是一种好习惯吗?

是的。看看 C++ STL(标准模板库)。你会发现大量的结构。

是否可以阻止结构声明一个虚拟方法,这将创建一个 vtable 并修改结构的大小?

没有.. 到目前为止.. 一旦你声明了虚函数.. 将为结构创建 vtable

【讨论】:

    【解决方案4】:

    结构与类

    您是对的,C++ 中 struct 和 class 之间的主要区别在于默认访问级别。如果没有显式访问修饰符,类成员是私有的,结构成员是公共的。结构成员也可以使用访问修饰符设为私有。记住;这也适用于继承的类和结构。

    至于一般建议:许多人仅将结构用于数据,而将类用于具有行为的所有事物 [1]。换句话说,用于 POD(普通旧数据)类型的结构 [2],这是一种普遍的做法。这并不意味着您不能拥有与访问和设置数据成员、设置构造函数、析构函数等相关的功能。“如果需要更多功能,则类更合适。如果有疑问,请将其设为类。”他们的指南还推荐函子和特征使用结构而不是类。

    您必须记住,除了任何技术上的优势或劣势之外,还有其他原因可以在团队中和基于项目的基础上执行特定的实践和标准。正如 Google 的风格指南中也提到的,我们可以为我们使用的数据结构添加语义。作为团队成员,我想知道结构是否具有行为。例如,很高兴知道所有结构都只是 POD 类型。

    Joint Strike Fighter 编码标准规定,“应该使用结构来为不需要不变量的实体建模。”而“应该使用一个类来对维护不变量的实体进行建模。”并且公共和受保护的数据只能在结构中使用,而不是在类中。他们这样做的理由是一个类不能控制对公共成员的访问;因此,一个类中的所有数据都应该是私有的。在决定编码标准时考虑您项目的需求。

    结构继承

    在考虑继承时,您必须考虑公共继承与私有继承的含义。请记住新的、派生的访问级别将具有哪些访问级别,以及继承您的结构是否有意义。结构成员可以设为私有,如果您从它继承,派生的将无法访问基的私有成员。

    struct base {   
        int public_data;
    
        private:
            int private_data;
    };
    
    struct derived : base { 
        derived() {
            public_data = 1;
            // private_data = 1;    // no access, won't compile
        }
    };
    

    换句话说,继承可能被认为是一个逻辑问题而不是实现问题。

    从技术上讲,从根本上来说,继承结构没有任何问题。这可能是一种仁慈的做法,在某些情况下,它可能是有益的并且很有意义。

    请记住,在 C++ 中,结构可以从类继承,反之亦然。

    有关 vtable 的更多信息,请参阅此问题:When is a vtable created in C++?

    [1]https://google.github.io/styleguide/cppguide.html#Structs_vs._Classes

    [2]http://en.cppreference.com/w/cpp/concept/PODType

    【讨论】:

      【解决方案5】:

      据我所知,C++ 中的类和结构之间的主要区别(并且可能是唯一的区别)是,类的成员默认为私有,而结构的成员默认为公共。

      使用关键字struct 声明的类与使用关键字class 声明的类之间的唯一区别确实是默认访问说明符(它也适用于基类和成员)。

      确实,理解结构最简单的方法就是理解它们是类。

      即使语言允许,继承结构是一种好习惯吗?

      当然。继承一个类就可以了,结构就是类。

      是否可以阻止结构声明一个虚拟方法,这将创建一个 vtable 并修改结构的大小?

      没有,据我所知没有这样的功能。

      【讨论】:

        【解决方案6】:

        鉴于struct A { ... };struct B { A a; ... }struct B : A { ... } 安全得多。

        我建议不继承比使用非虚拟析构函数继承要好。您唯一失去的是从B*B&amp;A*A&amp; 等的隐式转换。但是对于这些情况,您仍然拥有显式B b; b.a

        【讨论】:

        • 你应该解释你给定的。此外,这与struct 关键字没有任何关系。
        • A 是您想要的任何基本类型。这与没有所有虚拟方法的继承有很大关系。它同样适用于使用struct 定义的类类型,因为class 不会影响它。
        猜你喜欢
        • 1970-01-01
        • 2021-02-07
        • 2015-12-12
        • 2014-12-22
        • 1970-01-01
        • 1970-01-01
        • 2011-06-16
        • 1970-01-01
        • 2016-12-12
        相关资源
        最近更新 更多