【问题标题】:How should I order the members of a C++ class?我应该如何订购 C++ 类的成员?
【发布时间】:2010-09-23 10:36:11
【问题描述】:

拥有所有私有成员,然后是所有受保护成员,然后是所有公共成员会更好吗?还是反过来?还是应该有多个私有、受保护和公共标签,以便操作可以与构造函数等分开?做出此决定时应考虑哪些问题?

【问题讨论】:

  • 有趣的是,这些几乎完全基于意见的主题在“过去”受到热烈欢迎,而我认为并且希望它们现在会被遗忘。
  • 我现在倾向于同意这个,但我犹豫要不要删除它,因为它很受欢迎。
  • @underscore_d 我真的很想知道为什么,很多很多问题,很少有没有任何细节或清晰度的词,完全基于意见有 700 票。而今天同样的问题会得到 3-4 票反对并在几秒钟内立即关闭
  • 我猜有什么进展?
  • 这是所有 C++ 程序员都会问自己的问题。目前尚不清楚答案是否是“基于意见的”。我认为应该允许这样的问题存在,只要语气保持文明,只要还有一些微妙的问题需要提出,例如关于可读性或可维护性。

标签: c++ code-formatting


【解决方案1】:

二进制兼容性

类成员排序有几个具体原因。 这些与二进制兼容性有关。

二进制兼容性主要影响对系统 DLL 和设备驱动程序的更改。 如果您对这些不感兴趣,请忽略此答案。

公共成员必须先于私人成员。 这样您就可以在不影响公共数据位置的情况下混合和更改私有成员。

新的公共成员必须最后。 这再次避免了影响现有公共成员的位置。

同样的顺序也适用于 vtable 成员。

除此之外,没有理由不遵循您自己/您同事的偏好。

【讨论】:

    【解决方案2】:

    Google 青睐 this order:“Typedef 和枚举、常量、构造函数、析构函数、方法,包括静态方法、数据成员,包括静态数据成员。”

    Matthew Wilson(需要 Safari 订阅)推荐以下顺序:“Construction, Operations, Attributes, Iteration, State, Implementation, Members, and my favorite, Not to be implemented。”

    他们提供了很好的理由,这种方法似乎相当标准,但无论你做什么,都要保持一致。

    【讨论】:

      【解决方案3】:

      编码风格是令人惊讶的激烈对话的来源,考虑到这一点,我冒着提供不同意见的风险:

      代码应该写成对人类来说最易读。我完全同意这里多次给出的声明。

      偏差是我们正在进行的滚动。

      为了帮助班级的用户了解如何使用它,应该编写和维护适当的文档。用户永远不需要阅读源代码即可使用该类。如果这样做(手动或使用源内文档工具)完成,那么在源中定义公共和私有类成员的顺序对用户来说并不重要。

      但是,对于需要理解代码的人来说,在代码审查、拉取请求或维护期间,顺序非常重要 - 规则很简单:

      项目应在使用前定义

      这既不是编译器规则,也不是严格的公共 vs.私人规则,但常识 - 人类可读性规则。我们按顺序阅读代码,如果每次看到使用的类成员时都需要来回“玩弄”,但不知道它的类型,例如,它会对代码的可读性产生不利影响。

      严格按照私人与私人进行划分public 违反了这条规则,因为私有类成员在任何公共方法中使用后都会出现。

      【讨论】:

      • 如果可以的话,我会给予的不仅仅是我被允许给予的这一票。我自己说得再好不过了。如果您想使用,请使用您的 IDE 的“结构”选项卡或文档,如果您需要实际查看/理解此处提供的代码,这是最有意义的。
      【解决方案4】:

      把私有字段放在第一位。

      在现代 IDE 中,人们不需要阅读类来弄清楚它的公共接口是什么。

      他们只是为此使用智能(或类浏览器)。

      如果有人正在阅读类定义,通常是因为他们想了解它是如何工作的。

      在这种情况下,了解这些字段最有帮助。它告诉你对象的各个部分是什么。

      【讨论】:

      • 我可以同意有资格,但有点理解为什么这被否决了......并不是说选民屈尊解释这个问题。 资格:在编写类供我使用时,我倾向于这样做:顶部是私有字段,然后是私有函数;然后是底部的公共内容。显然调整了排序依赖项。而且我什至不使用 IDE,只使用vim!但是有一个问题:如果我编写类供其他人使用,我会在编写时考虑到它们,即预先考虑最相关的东西。这只是礼貌,尤其是如果他们也避开当前流行的任何 IDE
      • 我读了一堂课,看看如何使用它。只有在用法不清楚的情况下,我才会查看实现细节。许多人仍然使用 vim 和 emacs,并且无法访问 xwindows 来运行“现代”IDE。
      【解决方案5】:

      在实践中,它并不重要。这主要是个人喜好问题。

      把公共方法放在首位是很流行的,表面上是为了让类的用户更容易找到它们。但是标题永远不应该是您的主要文档来源,因此围绕用户将查看您的标题的想法建立“最佳实践”似乎对我来说没有意义。

      如果人们修改类,他们更有可能出现在您的标题中,在这种情况下,他们应该关心私有接口。

      无论您选择哪种方式,都应使您的标题简洁易读。能够轻松找到我碰巧正在寻找的任何信息,无论我是课程的用户还是课程的维护者,都是最重要的。

      【讨论】:

        【解决方案6】:

        总体而言,您的公共接口应该放在任何事情之前,因为这是您的类的用户应该感兴趣的主要/唯一的事情。(当然,实际上这并不总是成立,但这是一个好的开始。)

        其中,成员类型和常量是最好的,其次是构造运算符、操作,然后是成员变量。

        【讨论】:

          【解决方案7】:

          请注意(取决于您的编译器和动态链接器),您可以通过仅添加到类的末尾(即接口的末尾)来保持与以前版本的共享库的兼容性,而不是删除或更改还要别的吗。 (G++ 和 libtool 也是如此,GNU/Linux 共享库的三部分版本控制方案反映了这一点。)

          还有一个想法是您应该对类的成员进行排序以避免由于内存对齐而浪费空间;一种策略是将成员从最小到最大排序。不过,我从来没有在 C++ 或 C 中这样做过。

          【讨论】:

          • 我相信推荐实际上是从大到小排序,不是吗?我得再看看,也许我们可以找到参考。
          • Here 是顺序与对齐的好答案。
          【解决方案8】:

          与往常一样,首先为人类编写代码。考虑将使用您的课程的人,并将最重要的成员/枚举/typedefs/whatever 对他们放在顶部。

          通常这意味着公共成员位于顶部,因为这是您班级的大多数消费者最感兴趣的。其次是受保护的,其次是私人。通常。

          有一些例外。

          有时初始化顺序很重要,有时需要在公共之前声明私有。有时,继承和扩展一个类更为重要,在这种情况下,受保护的成员可能会放在更高的位置。而且,当对遗留代码进行单元测试时,有时公开公共方法会更容易——如果我不得不犯下这种近乎罪恶的行为,我会将它们放在类定义的底部。

          但它们是相对罕见的情况。

          我发现大多数时候“公共、受保护、私有”对您所在班级的消费者最有用。这是一个不错的基本规则。

          但它不是关于按访问排序,而是更多关于按消费者的兴趣排序

          【讨论】:

            【解决方案9】:

            我倾向于关注POCO C++ Coding Style Guide

            【讨论】:

              【解决方案10】:

              这对那些将使用您的类首先列出公共接口的人非常有帮助。这是他们关心并可以使用的部分。受保护的和私有的可以跟进。

              在公共接口中,可以方便地将构造函数、属性访问器和修改器以及运算符分组到不同的组中。

              【讨论】:

                【解决方案11】:

                在我们的项目中,我们不是根据访问权限对成员进行排序,而是根据使用情况对成员进行排序。我的意思是,我们在使用成员时对其进行排序。如果公共成员使用同一类中的私有成员,则该私有成员通常位于公共成员前面的某个位置,如下面(简单)示例:

                class Foo
                {
                private:
                  int bar;
                
                public:
                  int GetBar() const
                  {
                    return bar;
                  }
                };
                

                这里,成员 bar 放在成员 GetBar() 之前,因为前者被后者使用。这可能会导致多个访问部分,如下例所示:

                class Foo
                {
                public:
                  typedef int bar_type;
                
                private:
                  bar_type bar;
                
                public:
                  bar_type GetBar() const
                  {
                    return bar;
                  }
                };
                

                bar_type 成员被 bar 成员使用,明白吗?

                这是为什么?我不知道,如果您在实现中的某个地方遇到一个成员并且您需要有关该成员的更多详细信息(并且 IntelliSense 又被搞砸了),您可以在上面工作的地方找到它,这似乎更自然。

                【讨论】:

                  【解决方案12】:

                  我通常首先定义接口(要读取),即公共的,然后是受保护的,然后是私有的东西。现在,在许多情况下,我向前迈进了一步,并且(如果可以的话)使用 PIMPL 模式,将所有私有内容完全隐藏在真实类的接口中。

                  class Example1 {
                  public:
                     void publicOperation();
                  private:
                     void privateOperation1_();
                     void privateOperation2_();
                  
                     Type1 data1_;
                     Type2 data2_;
                  };
                  // example 2 header:
                  class Example2 {
                     class Impl;
                  public:
                     void publicOperation();
                  private:
                     std::auto_ptr<Example2Impl> impl_;
                  };
                  // example2 cpp:
                  class Example2::Impl
                  {
                  public:
                     void privateOperation1();
                     void privateOperation2();
                  private: // or public if Example2 needs access, or private + friendship:
                     Type1 data1_;
                     Type2 data2_;
                  };
                  

                  您会注意到我在私有(以及受保护的)成员后面加上下划线。 PIMPL 版本有一个内部类,外部世界甚至看不到它的操作。这使类接口完全干净:只暴露真实的接口。无需争论秩序。

                  在类构建过程中存在相关成本,因为必须构建动态分配的对象。此外,这对于不打算扩展但在层次结构方面存在一些缺点的类也非常有效。受保护的方法必须是外部类的一部分,因此您不能真正将它们推入内部类。

                  【讨论】:

                    【解决方案13】:

                    我把公共接口放在首位,但我并不总是这样做。我过去常常这样做,先是私有的,然后是受保护的,然后是公开的。回想起来,这并没有多大意义。

                    作为一个类的开发者,您可能很熟悉它的“内部结构”,但该类的用户并不在意,或者至少他们不应该在意。他们最感兴趣的是班级能为他们做什么,对吧?

                    所以我将公众放在首位,并通常按功能/效用来组织它。我不希望他们必须费力地浏览我的界面才能找到与 X 相关的所有方法,我希望他们以有条理的方式看到所有这些东西。

                    我从不使用多个公共/受保护/私人部分 - 在我看来太混乱了。

                    【讨论】:

                    • 这只是我的意见,但我不同意一个类的用户不应该关心内脏。我认为用户应该关心,因为抽象和封装只是为了解决复杂的问题,而不是让用户不必处理细节。
                    • 感谢您的评论,戴夫。如果我正在评估课程的效率或“如何”,或者如果我担心它不正确,那么我会关心内部问题,但主要是作为课程的用户,我'我关心的是类的行为,而不是它内部管理的方式。
                    • @itsmatt sizeof(object) 如果我没记错的话,取决于成员的顺序。如果是这样,它对订单没有影响吗?假设我有一个 private 以及 public 和其他 char 类型变量的 double,我需要将 double 变量放在一起吗?这种情况我们怎么处理?
                    • 这完全是基于双方观点的观点。从private 开始,允许从隐藏的数据成员构建类的客户端可以做什么——一种逻辑顺序。从public 开始,阅读该课程的人可以立即看到其公共界面,并在公共部分结束后立即停止阅读 - 有点对读者来说更容易
                    【解决方案14】:

                    完全取决于您的喜好。没有“正确的方法”。

                    在我自己的宠物项目中使用 C++ 时,我个人遵守惯例,即在每个成员或方法声明之前放置访问修饰符。

                    【讨论】:

                    • 我没有标记你,但我怀疑有些人这样做了,因为在每个成员之前放置一个访问修饰符是不必要的并且是多余的。坦率地说,我发现由于增加了噪音,它会影响易读性。
                    • 它让你的 C++ 看起来像 Java。那么问题是Java的“每个声明键入一个额外的单词”是否比C ++的“访问说明符更改全局状态,每个声明都隐式使用”更好或更差。即使你更喜欢 Java,你是否应该在 C++ 中使用 C++ 方式。
                    • 我不明白为什么到目前为止有人因为敢于发表意见而被否决,以回应一个几乎完全基于意见的问题 - 一个明确的标记原因,我喜欢想一想,在今天可能改进得更好的 SO 中,甚至都不会起步。
                    【解决方案15】:

                    我认为这一切都与可读性有关。

                    有些人喜欢按固定顺序对它们进行分组,这样每当您打开一个类声明时,您就可以快速知道在哪里查找,例如公共数据成员。

                    总的来说,我觉得最重要的事情应该放在第一位。对于所有类中的 99.6%,粗略来说,这意味着公共方法,尤其是构造函数。然后是公共数据成员,如果有的话(记住:封装是个好主意),然后是任何受保护的和/或私有的方法和数据成员。

                    这是大型项目的编码标准可能涵盖的内容,检查一下可能是个好主意。

                    【讨论】:

                      【解决方案16】:

                      这是我的看法,我敢打赌,大多数人都会同意,公共方法应该优先考虑。 OO 的核心原则之一是您不必关心实现。只需查看公共方法,您就可以了解使用该类所需了解的一切。

                      【讨论】:

                      • IMO,OOP 原则与您编写公共成员的位置无关。
                      猜你喜欢
                      • 1970-01-01
                      • 2020-06-27
                      • 2016-01-12
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2010-11-13
                      相关资源
                      最近更新 更多