我该如何订购C ++类的成员?

时间:2008-11-21 12:13:01

标签: c++ code-formatting

拥有所有私人成员,然后是所有受保护的成员,然后是所有公共成员,是否更好?或者相反?或者是否应该有多个私有,受保护和公共标签,以便操作可以与构造函数分开等等?在做出这个决定时我应该考虑哪些问题?

15 个答案:

答案 0 :(得分:48)

我把公共接口放在首位,但我并不总是这样做。我常常向后做这件事,私有,然后保护,然后公开。回顾过去,它没有多大意义。

作为一个班级的开发者,你可能很熟悉它的“内脏”,但是班级的用户并不在乎,或者至少他们不应该这么做。他们最感兴趣的是班级可以为他们做些什么,对吧?

所以我把公众放在首位,并通常通过功能/实用程序来组织它。我不希望他们不得不通过我的界面来找到与X相关的所有方法,我希望他们能够以有条理的方式看到所有这些东西。

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

答案 1 :(得分:37)

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

Matthew Wilson(需要Safari订阅)推荐以下顺序:“构造,操作,属性,迭代,状态,实现,成员和我最喜欢的,不实现。”

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

答案 2 :(得分:8)

这是我的观点,我猜赌大多数人会同意,公共方法应该先行。 OO的核心原则之一是您不必关心实施。只要查看公共方法,就应该告诉您使用该类需要了解的所有内容。

答案 3 :(得分:5)

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

通常这意味着公共成员处于最顶层,因为这是您班级中大多数消费者最感兴趣的内容。接下来是受保护的私人。一般

有一些例外。

有时初始化订单很重要,有时私人需要在公众面前宣布。有时候,继承和扩展一个类更重要,在这种情况下,受保护的成员可能会被放置得更高。有时候对遗留代码进行黑客单元测试时,暴露公共方法会更容易 - 如果我必须犯下这种近似罪行,我会把它们放在类定义的底部。

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

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

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

答案 4 :(得分:4)

我通常首先定义接口(要读取),即公共接口,然后是受保护接口,然后是私有接口。现在,在许多情况下,我向前迈出一步(如果我能处理它)使用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版本有一个内部类,外部世界甚至都看不到这些操作。这样可以使类接口完全干净:只暴露实际接口。无需争论秩序。

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

答案 5 :(得分:3)

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

应编写代码,使其对人类最具可读性。我完全同意这里多次发表的声明。

偏差是我们采取的滚动。

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

但是,对于需要理解代码的人,在代码审核,提取请求或维护期间,订单非常重要 - 规则很简单:

项目应在使用前定义

这既不是编译器规则,也不是严格的公共版本。私人统治,但常识 - 人类可读性规则。我们按顺序阅读代码,如果需要&#34; juggle&#34;每次我们看到使用的类成员来回来回,但不知道它的类型,例如,它会对代码的可读性产生负面影响。

严格按私人诉讼进行分组public违反了此规则,因为私有类成员在用于任何公共方法后都会出现。

答案 6 :(得分:2)

我认为这完全取决于可读性。

有些人喜欢按固定顺序对它们进行分组,因此无论何时打开课程声明,您都会很快知道在哪里寻找,例如:公共数据成员。

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

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

答案 7 :(得分:2)

我倾向于遵循POCO C++ Coding Style Guide

答案 8 :(得分:2)

在实践中,它很少重要。这主要取决于个人偏好。

将公共方法放在第一位是非常受欢迎的,表面上是这样的,以便该类用户能够更容易地找到它们。但是标题永远不应该是您文档的主要来源,因此围绕用户将查看标题的想法的“最佳实践”似乎错过了我的标记。

如果人们修改类,那么人们更有可能进入你的标题,在这种情况下,他们关心私人界面。

无论您选择哪种方式,都可以使标题清晰易读。能够轻松找到我正在寻找的任何信息,无论我是班级用户还是班级维护者,都是最重要的事情。

答案 9 :(得分:1)

在我们的项目中,我们不会根据访问次数订购会员,而是按使用情况订购会员。我的意思是,我们按照使用的顺序对成员进行排序。如果公共成员在同一个类中使用私有成员,那么该私有成员通常位于某个公共成员的前面,如下面的(简单化)示例所示:

class Foo
{
private:
  int bar;

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

此处,成员位于成员 GetBar()之前,因为前者由后者使用。这可能会导致多个访问部分,如下例所示:

class Foo
{
public:
  typedef int bar_type;

private:
  bar_type bar;

public:
  bar_type GetBar() const
  {
    return bar;
  }
};

bar 成员使用 bar_type 成员,请参阅?

这是为什么?我不知道,如果你在实现的某个地方遇到一个成员并且你需要更多关于它的细节(并且再次搞砸了IntelliSense),你可以在你工作的地方找到它,这似乎更自然。

答案 10 :(得分:1)

对于那些将使用您的类首先列出公共接口的人来说真的很有帮助。这是他们关心和可以使用的部分。保护和私人可以跟随。

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

答案 11 :(得分:1)

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

还有一个想法是你应该命令类的成员避免由于内存对齐而浪费的空间;一种策略是订购从最小到最大的成员。我从来没有用C ++或C做过这个。

答案 12 :(得分:1)

总的来说,你的公共界面应该先于什么,因为这是你的课程用户应该感兴趣的主要/唯一的事情。(当然,实际上并不总是如此,但这是一个良好的开端。)

在其中,首先是成员类型和常量,然后是构造运算符,运算,然后是成员变量。

答案 13 :(得分:-1)

首先放置私有字段。

使用现代IDE,人们不会阅读课程来弄清楚它的公共界面是什么。

他们只是使用intellisence(或类浏览器)。

如果有人正在阅读课程定义,通常是因为他们想要了解它的工作原理。

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

答案 14 :(得分:-3)

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

在我自己的宠物项目中做C ++时,我个人保持约定,我在每个成员或方法声明之前放置了访问修饰符。