简体   繁体   English

如何在C ++中订购函数?

[英]How to order functions in C++?

I'm not sure how to order my functions in C++. 我不知道如何在C ++中订购我的函数。 In C, I simply placed a function that uses another function below that function, as closely as possible - that's pretty common. 在C中,我只是放置了一个函数,该函数使用该函数下面的另一个函数,尽可能接近 - 这很常见。 Like this: 像这样:

void bar()
{
}

void foo()
{
    bar();
}

However, in C++, there are several types of functions: 但是,在C ++中,有几种类型的函数:

  • Free functions 免费功能
  • Private member functions 私人会员功能
  • Public member functions 公共成员职能
  • Static member functions 静态成员函数

I'm currently making my function order dependent on how they are ordered in the .hpp file, eg: 我目前正在使我的函数顺序依赖于它们在.hpp文件中的排序方式,例如:

class Foo_bar {
public:
    Foo_bar();
    void foo();
private:
    int some_member;
    void bar();

But now, if the constructor uses foo() or bar(), these will be below the constructor in the source file, inconsistent with my usual ordering. 但是现在,如果构造函数使用foo()或bar(),它们将位于源文件的构造函数下方 ,与我通常的顺序不一致。 I could of course reorder my header to take account of that: 我当然可以重新排序我的标题以考虑到这一点:

class Foo_bar {
private:
    int some_member;
    void bar();

public:
    void foo();
    Foo_bar();

But I think that's a mess. 但我认为那是一团糟。

Furthermore, in Java, the opposite to my first example seems to be common: 此外,在Java中,与我的第一个示例相反似乎很常见:

void foo()
{
    bar();
}

void bar()
{
}

That's probably due to the top-down thinking common in OOP, in contrast to the bottom-up thinking common in procedural/functional programming. 这可能是由于OOP中常见的自上而下的思维,与程序/函数式编程中常见的自下而上的思维形成鲜明对比。 However, with free functions that don't have prototypes, this top-down style is not possible. 但是,对于没有原型的自由函数,这种自上而下的风格是不可能的。

Is it even possible to order functions in C++ in a consistent way? 甚至可以以一致的方式在C ++中命令函数吗?

It's possible. 这是可能的。 You have to use forward declaration . 你必须使用前瞻声明

Declare a function before defining it, and other functions will see it without problem even if they are defined before. 在定义函数之前声明一个函数,即使之前定义了函数,其他函数也会看到它没有问题。

So, you should be able to do this in C++: 所以,你应该能够用C ++做到这一点:

void bar();  // forward declaration; note that function bar isn't defined yet

void foo()
{
    bar();   // foo knows that bar is declared, so it will search for bar's definition
}

void bar()   // here bar is defined, so foo will use this when needed
{
}

It's a pretty good question actually, because readability has a major impact on whoever will read the code after you. 实际上这是一个非常好的问题,因为可读性会影响到你之后阅读代码的人。

There are 3 kinds of people who will read the code of a class: 有3种人会阅读课程代码:

  • those who wish to uses it (and don't care much about its internals) 那些希望使用它的人(并不关心它的内部)
  • those who wish to inherit from your class (and don't care much about its internals) 那些希望从你的班级继承的人(并不关心它的内部)
  • those who wish to hack on your class, and thus really care of its internals 那些希望攻击你的课程,从而真正关心其内部的人

For this reason, I try to order the headers so that any user may stop once he got what he was looking for, which means: 出于这个原因,我尝试订购标题,以便任何用户可以在他得到他想要的东西后停止,这意味着:

class Foo
{
public:
  // types
  // static methods
  // methods (usually constructors,
  //          then simple accessors,
  //          then more complicated stuff)

protected:
  // same pattern

private:
  // same pattern
  // attributes
};

// Free functions about this class

// Implementation of inline / template methods

Sometimes I need to declare some types beforehand even though they are private, but this is rare. 有时我需要事先声明一些类型,即使它们是私有的,但这种情况很少见。 The goal of this ordering is to absolutely minimize the amount of stuff a reader has to read before he gets what he wants (and stops reading and goes back to what he was doing before having to interrupt himself to look at your code). 这种排序的目的是绝对减少读者在得到他想要的东西之前必须阅读的内容量(并且在不得不打断自己查看代码之前停止阅读并回到他正在做的事情)。

Then, regarding "helper" methods, it depends on the type of code: 然后,关于“帮助”方法,它取决于代码的类型:

  • for template code, I use a "details" namespace, it's both clear to the reader that he should not be worried about it and it isolate the names in their own namespace so that they do not pop up in code completion tools 对于模板代码,我使用“详细信息”命名空间,读者都清楚他不应该担心它并且它将名称隔离在它们自己的命名空间中,这样它们就不会弹出代码完成工具
  • for regular code, I use an anonymous namespace within the source file, which is even better since then it actually generates invisible symbols and I don't run the risk of violating ODR . 对于常规代码,我在源文件中使用匿名命名空间,这甚至更好,因为它实际上生成了不可见的符号,并且我没有冒违反ODR的风险。

If some code may require a lot of helpers, I tend to create a dedicated header file in the source directory, giving the following structure: 如果某些代码可能需要很多帮助程序,我倾向于在源目录中创建一个专用的头文件,给出以下结构:

include/
  foo.hpp

src/
  fooImpl.hpp --> #include "foo.hpp"
  foo.cpp     --> #include "fooImpl.hpp"

in order to provide a list of declarations to the reader, because it's easier to browse a list of declarations than to extract the declarations from a list of definitions, whatever the indentation and style. 为了向读者提供声明列表,因为浏览声明列表比从定义列表中提取声明更容易,无论缩进和样式如何。

And of course, always to make it easier, I always order the list of declarations and the list of definitions equally... 当然,总是为了让它更容易,我总是同样地命令声明列表和定义列表......

You declare the class in a header file, right? 你在头文件中声明了这个类,对吧? And implement most of it in a separate file? 并在一个单独的文件中实现其中的大部分? If you simply implement the constructor in the implementation file and not in the header, I don't think you'll experience the problem you mentioned (because the entire header will be seen before the constructor is seen to call foo() or bar() . 如果你只是在实现文件中而不是在头文件中实现构造函数,我认为你不会遇到你提到的问题(因为在看到构造函数调用foo()bar()之前会看到整个头文件bar()

Ordering free functions in C++ obeys the same rules as you mentioned, but like darioo said, you can forward declare them and order the function definitions any way you want. 在C ++中排序自由函数遵循与您提到的相同的规则,但是像darioo所说的那样,您可以转发声明它们并以您想要的任何方式排序函数定义。 That is also the preferred way: declare everything in header, and put ALL definitions in the source file. 这也是首选方法:在头文件中声明所有内容,并将所有定义放在源文件中。 This is not possible for templates though, without some non-trivial and non-general anti-template workarounds. 但是,对于模板,这是不可能的,没有一些非平凡和非一般的反模板解决方法。

In a class, things are usually different, because there is almost no cases where you fully implement your class in a header, thus the declarations are always read when you're defining the functions in the source file. 在类中,事情通常是不同的,因为几乎没有在标题中完全实现类的情况,因此当您在源文件中定义函数时,始终会读取声明。

I usually order functions in "function", and group eg. 我通常在“功能”中订购功能,例如。 getters and setters, constructor/destructor (if possible). getter和setter,构造函数/析构函数(如果可能的话)。

Your concern about reordering the functions in the class definition is not correct as clarified by the following two quotes from the C++03 Standard. 关于重新排序类定义中的函数的问题是不正确的,正如C ++ 03标准中的以下两个引号所阐明的那样。

$9.2/2- "A class is considered a completely-defined object type (3.9) (or complete type) at the closing } of the class-specifier. Within the class member-specification, the class is regarded as complete within function bodies, default arguments and constructor ctor-initializers (including such things in nested classes). Otherwise it is regarded as incomplete within its own class member-specification." $ 9.2 / 2-“类在类别说明符的结束时被认为是一个完全定义的对象类型(3.9)(或完整类型)。 在类成员规范中,该类在函数体内被认为是完整的,默认参数和构造函数ctor-initializers(包括嵌套类中的这类东西)。否则它在自己的类成员规范中被认为是不完整的。“

And

$3.4.1/8 - "A name used in the definition of a member function (9.3) of class X following the function's declaratorid29) shall be declared in one of the following ways: $ 3.4.1 / 8 - “在函数的declaratorid29之后的类X的成员函数(9.3)的定义中使用的名称应以下列方式之一声明:

— before its use in the block in which it is used or in an enclosing block (6.3), or - 在其使用的块中或在封闭块(6.3)中使用之前,或

— shall be a member of class X or be a member of a base class of X (10.2), or - 应为X类成员或X(10.2)基类的成员,或

— if X is a nested class of class Y (9.7), shall be a member of Y, or shall be a member of a base class of Y (this lookup applies in turn to Y's enclosing classes, starting with the innermost enclosing class),30) or - 如果X是类Y(9.7)的嵌套类,则应该是Y的成员,或者应该是Y的基类的成员(此查找依次应用于Y的封闭类,从最内层的封闭类开始) ,30)或

— if X is a local class (9.8) or is a nested class of a local class, before the definition of class X in a block enclosing the definition of class X, or - 如果X是本地类(9.8)或是本地类的嵌套类,则在包含X类定义的块中定义类X之前,或者

— if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within a local class of a function that is a member of N, before the member function definition, in namespace N or in one of N's enclosing namespaces. - 如果X是名称空间N的成员,或者是作为N的成员的类的嵌套类,或者是作为N的成员的函数的本地类中的本地类或嵌套类,则成员函数定义,在名称空间N中或在N的封闭名称空间之一中。

As a general rule, in C++, function definitions have to be visible at the point of their use. 作为一般规则,在C ++中,函数定义必须在其使用时可见。 The only exception is the case of class member functions as illustrated by the above quotes. 唯一的例外是类成员函数的情况,如上面的引号所示。

Therefore, this means that the class member functions to be called by the constructor need not be defined before the constructor lexically. 因此,这意味着构造函数调用的类成员函数不需要在构造函数的词法之前定义。

Personally, I like to see things that will be referred to from elsewhere (which people will need to find/read often) near the top of the file. 就个人而言,我喜欢在文件顶部附近看到将从别处引用的内容(人们需要经常查找/阅读)。 Internals that, once stable, can hopefully be forgotten are left for later. 一旦稳定,有希望被遗忘的内部构件将留待以后使用。

There's inconsistencies, though. 但是存在不一致之处。 For example, in a class, it implies putting public stuff first, private internals later. 例如,在一个类中,它意味着先将公共内容放入私有内部。 But the default visibility for a class (what you naturally get) is private, and (particularly if I have inline-style methods) I generally put any private data in front. 但是类的默认可见性(你自然得到的)是私有的,并且(特别是如果我有内联样式方法)我通常会将任何私有数据放在前面。 It may even be an error for an inline-style method to reference a member variable before it has been defined - sorry, I'm suffering a temporary memory issue. 内联样式方法在定义之前引用成员变量甚至可能是错误的 - 抱歉,我遇到了临时内存问题。

But basically, the main thing is to put things together than are similar or logically related. 但基本上,主要是将事物放在一起而不是相似或逻辑相关。 A begin method will be adjacent to an end method, a Step_To_Next method adjacent to a Step_To_Prev method, etc. Grouping be similar purposes, similar parameters, and commonly being used together are all good. begin方法将与end方法相邻,与Step_To_Prev方法相邻的Step_To_Next方法等。分组是相似的目的,类似的参数,并且通常一起使用都是好的。

What calls what is mostly an implementation detail, so not something you should necessarily emphasise in header files that the user of your library will read - though in implementation code things may be different. 什么叫做主要是一个实现细节,所以不一定要强调你的库用户会读取的头文件 - 尽管在实现代码中可能会有所不同。 As others have pointed out, forward declarations allow some freedom with this. 正如其他人所指出的那样,前瞻性声明允许一些自由。

Most important (1) adopt a consistent style, and (2) don't worry too much about ambiguous cases. 最重要的是(1)采用一致的风格,(2)不要过于担心模糊的情况。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM