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


73

最好让所有私人成员,然后是所有受保护的成员,然后是所有公共成员?还是相反?还是应该有多个私有的,受保护的和公共的标签,以便可以将操作与构造函数分开,以此类推?做出此决定时应考虑哪些问题?


10
有趣的是,这些“几乎完全基于意见的线索”是如何在“一天回来”的时候受到热烈欢迎的,而我想并且希望它们如今会被遗忘。
underscore_d

1
我现在倾向于同意这一点,但是由于它非常流行,因此我还是在删除它。
汤米·赫伯特

@underscore_d我真的很奇怪,为什么很多问题,很少有细节或不清楚的单词,完全基于意见的评论有700份赞成票。而今天相同的问题将需要3-4次投票,并在几秒钟内立即结束
Aryan Parekh

1
我想进步了吗?
汤米·赫伯特

1
所有C ++程序员都会在某个时候问自己一个问题。尚不清楚答案是否是“基于意见的”。我认为,只要语气保持平和,并且还有一些微妙的要点,例如关于可读性或可维护性,就应该允许这样的问题存在。
Elling

Answers:


55

我把公共接口放在第一位,但是我并不总是这样做。我曾经做过相反的事情,先是私有的,然后是受保护的,然后是公开的。回顾过去,这没有多大意义。

作为班级的开发人员,您可能会很熟悉班级的“内脏”,但是班级的用户并不太在乎,或者至少他们不应该这样做。他们对课程可以为他们做什么最感兴趣,对吗?

因此,我将公众放在第一位,并通常按功能/效用对其进行组织。我不希望他们必须通过我的界面来查找与X相关的所有方法,我希望他们以有组织的方式将所有这些东西放在一起。

我从不使用多个公共/受保护/私有部分-以我的观点太混乱了。


1
这只是我的意见,但我不同意类的用户不应该在意内部。我认为用户应该在意,因为那里的抽象和封装只允许解决复杂的问题,而不是使用户不必处理细节。
Dave Van den Eynde

9
谢谢你的评论,戴夫。如果我要评估某个班级的效率或“如何”,或者如果我担心班级不正确,那我将关心内在因素,但主要是作为班级的用户,我我关心的是班级的行为,而不是班级如何在内部管理事情。
itsmatt

1
如果我没有记错的话,@ itsmatt sizeof(object)取决于成员的顺序。如果是这样,对订单没有影响吗?假设我在私人和公共场所都有一个double,然后在其他char类型变量中拥有一个double,是否需要将double变量放在一起?在这种情况下,我们该如何处理?
Rajesh

这完全是基于双方意见的观点。从开始private允许人们从隐藏的数据成员建立到类的客户能做的-逻辑上的顺序。从头开始,public允许阅读班级的人立即看到其公共界面,并在公共部分结束后立即停止阅读,这对读者来说更容易些
PetrVepřek'5

41

Google赞成以下顺序:“ Typedef和枚举,常量,构造函数,析构函数,方法(包括静态方法),数据成员(包括静态数据成员)”。

马修·威尔逊(需要Safari订阅)建议以下顺序:“构造,操作,属性,迭代,状态,实现,成员以及我最喜欢的,未实现。”

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


9

这是我的观点,我猜想大多数人都会同意,公共方法应该优先。OO的核心原则之一就是您不必关心实现。仅查看公共方法应该告诉您使用该类所需的一切。


IMO,OOP原则与编写公共成员的位置无关。
瓦西里斯

7

编码风格是令人惊讶的激烈对话的源泉,因此请注意,我可能会提出不同的意见:

应该编写代码,以使人类最容易阅读。我完全同意这里多次提出的这一说法。

偏差是我们要走的那一卷。

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

但是,对于需要了解的人代码的,在代码审阅,提取请求或维护期间,顺序很重要-规则很简单:

项目应在使用前定义

这既不是编译器规则,也不是严格的公共规则与私有规则,而是常识-人类可读性规则。我们按顺序读取代码,并且如果每次看到类成员都需要来回“打杂”,但是例如不知道其类型,则会对代码的可读性产生不利影响。

严格按私有与公共划分是违反此规则的,因为私有类成员将在任何公共方法中使用后出现。


1
如果可以的话,我将不仅仅给予这一票。我自己不能说得更好。如果您想使用,请使用您的IDE的“结构”选项卡或文档,如果您实际上需要查看/理解代码,那么这里介绍的内容最有意义。
scravy

5

与往常一样,请先为人类编写代码。考虑将要使用您的班级的人,并将最重要的成员/枚举/ typedefs /任何对他们来说放在顶部。

通常这意味着公众成员是最重要的,因为这是您班上大多数消费者最感兴趣的部分。其次是私有,然后是受保护的。通常。

有一些例外。

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

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

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

但是,这与按访问顺序排序无关,而更多地是根据消费者的兴趣进行排序


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版本具有内部类,外界甚至看不到该内部类的操作。这样可以使类接口完全干净:仅公开真实接口。无需争论秩序。

由于必须构建动态分配的对象,因此在类构造期间存在相关成本。对于那些不打算扩展的类,但在层次结构中存在一些不足的类,这也确实适用。受保护的方法必须是外部类的一部分,因此您不能真正将它们推入内部类。



2

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

有些人喜欢将它们按固定的顺序分组,这样,每当您打开类声明时,您便会快速知道在哪里寻找例如公共数据成员。

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

大型项目的编码标准可能涵盖了这些内容,请检查一下。


2

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

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_type构件用于通过件,看到了吗?

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


2

实际上,这并不重要。这主要是个人喜好问题。

表面上将公共方法放在首位是很受欢迎的,这样一来,该类的用户将可以更轻松地找到它们。但是标头永远都不应该是您主要的文档来源,因此基于“最佳实践”的想法,即用户将查看您的标头,这似乎对我没有帮助。

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

无论您选择哪种格式,都应使标题整洁且易于阅读。最重要的事情是,无论我是班级的用户还是班级的维护者,都能轻松找到我想查找的任何信息。


1

这对于使用您的课程首先列出公共界面的人们真的很有帮助。这是他们关心并可以使用的部分。受保护的和私有的可以跟随。

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


1

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

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


我认为建议实际上是按从大到小的顺序排列的,不是吗?我不得不再次查看,也许我们可以找到参考。
丹·奥尔森,2009年

是顺序与对齐的好答案。
Jacek Sieka 2012年

1

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

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


1

首先将私有字段放在首位。

使用现代IDE,人们无需阅读该类就可以弄清楚它的公共接口是什么。

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

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

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


我可以同意有资格的人,但可以理解为什么这被否决了……不是说选民打算解释这个问题。资格:在为我使用的类编写代码时,我倾向于做这样的事情:顶部是私有字段,然后是私有函数;然后是公共内容。显然调整排序依存关系。而且,我什至不使用IDE vim!但是存在一个问题:我是在编写供其他人使用的类时,我会牢记在心,即预先准备最相关的东西。那只是有礼貌,尤其是如果他们也避开了当前正在流行的IDE的话
underscore_d

我读了一堂课,看看如何使用它。仅当用法不清楚时,我才查看实现细节。许多人仍然使用vim和emacs,并且没有访问xwindows来运行“现代” IDE的权限。
edwinc

-3

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

在自己的宠物项目中使用C ++时,我个人遵守以下约定:我将访问修饰符放在每个成员或方法声明之前。


我没有给您打分,但我怀疑有些人这样做了,因为在每个成员之前放置一个访问修饰符是不必要的,而且很重要。坦白地说,我发现由于增加了噪音,它会影响可读性。
MattyT'

2
它使您的C ++看起来像Java。然后的问题是Java的“为每个声明键入一个额外的单词”是否比C ++的“访问说明符更改全局状态(每个声明隐式使用的)”好还是坏。另外,即使您更喜欢Java,是否也应该使用C ++的C ++方式进行操作。
史蒂夫·杰索普

1
我不明白为什么到目前为止有人因敢于发表意见而被否决了,以回应几乎完全基于观点的问题-我想认为,明确地举报理由是什至不会在今天大概更好的SO中脱颖而出。
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.