C ++什么可以放入类声明中,什么不可以

时间:2011-03-01 19:36:56

标签: c++

我完全糊涂在哪里放构造函数的定义。有时你会看到类似这样的东西

// point.h
class Point {
  Point(int x, int y) {
    x_ = x;
    y_ = y;
  }
private:
  const int x_;
  const int y_;
}

然后你有时会看到这样的事情:

// point.h
class Point {
  Point(int x, int y);
private:
  const int x_;
  const int y_;
}

// point.cc
Point::Point(int x, int y) {
  x_ = x;
  y_ = y;
}

即。有时像构造函数,复制构造函数等在.h中声明,然后在.cc文件中实现,有时它们会在标题中定义,依此类推。 但在哪种情况下呢?哪个是好习惯,哪个不是?

5 个答案:

答案 0 :(得分:5)

与某些答案相反,这两种做法存在差异。

如果将实现放入类声明中,该方法将自动标记为inline。对于像包装器这样的非常短的方法,这是一个可取的练习非常有用的功能。

如果您将实现放入单独的.cc文件中,那么您可以提高可读性(正如David Titarenco已经在他的回答中写的那样)并减少了项目中可能存在的依赖关系。仅在方法声明中作为引用,指针或返回值出现的类只需要在标头中使用前向声明。它们的实现细节在这里无关紧要,因此对于包含此标题的每个文件也是如此。一个例子:

// test.h
class A; // forward declaration of A
// #include "decl_of_class_A.h" // not needed here, because ...

class B {
public:
  A method1();        // in these cases the forward
  void method2(A* a); // declaration of A above 
  void method3(A& a); // is completely sufficient.
};

// test.cc
#include "decl_of_class_A.h" // here we really need
                             // the complete declaration of A
                             // in order to be able to *use* it

A B::method1() { /*...*/ }
// ...

现在,如果您将test.h包含到另一个.cc文件中并更改了类A的声明,则只需要重新编译test.cc而不需要重新编译.cc B文件。班级inline的声明没有改变。

除此之外:在某些情况下,您必须将实现放在标题中:如果您正在编写类或函数模板,或者确实希望将函数声明为const

正如Omnifarious所评论的那样:您有时看到的附加代码肯定会被每个C ++编译器拒绝。必须在初始化列表中初始化类的Point p(10, 20); Point q(0, 0); q = p; // no assignment operator provided ==> error! 个成员。之后它们会保持固定状态,并且不会为您提供默认的赋值运算符。这意味着即使您使用的是初始化列表,只要不重写赋值运算符,您就永远无法编写类似的东西:

const Point p(10, 20)

如果你真的需要恒定坐标,你应该写{{1}}。

答案 1 :(得分:2)

通常最好将声明保留在源代码中的标头和实现中。话虽如此,完全取决于作者。

只要它可读:)

答案 2 :(得分:1)

如果使用头文件,通常最好只在头文件中定义方法,并在cpp中实现它们。

您可以在类定义的内部或外部定义类方法(和构造函数/析构函数)。这两者都没有问题,只是风格问题。

差异仅在于语法,因为如果在类之外定义方法,则必须在方法名称之前添加TheClassName::(但在类型之后)。

答案 3 :(得分:1)

除非

,否则应将所有函数实现保留在头文件之外
  • 你必须因为它们是模板
  • 您希望它们被内联,而您的编译器不会执行LTCG。

一个干净的头文件非常有用,可以快速了解一个类的功能以及你应该如何使用它。

答案 4 :(得分:1)

当然,这两种定义都不是最好的方法。虽然这只与你的问题有外在联系。

答案是,如果方法非常短并且不会使类定义混乱,则应该使用第一种定义方式。类中的第一个样式具有在定义前面放置隐式inline关键字的效果。

第二种样式也可以在头文件中声明,您可以使用显式inline关键字。如果方法更复杂,或者它是模板规范的一部分,其中需要提供完整定义,那么这种情况就更常见,因此可以使用当前编译器正确地实例化模板。

因此,大多数情况下,您使用的是哪种定义。虽然如果使用第二种样式,类声明样式的外部,并且您的方法在头文件中声明,您的方法应该显式内联声明或者是模板类的成员。否则,链接器最终可能会给出重复的定义警告。

最后,以下是该方法的真正定义:

class Point {
  Point(int x, int y) : x_(x), y_(y) { }
private:
  const int x_;
  const int y_;
};

这是因为成员是const。它们必须在构造函数初始化列表中初始化,而不是在构造函数体内。在构造函数体中,这些赋值是非法的,因为成员是const

不管它们是否const,总是使用初始化列表来初始化成员是一个好习惯。这是因为如果在构造函数体中使用赋值,则最终调用该成员的默认构造函数,然后为其分配内容。这可能比仅仅使用适当的值初始化它更昂贵,特别是如果类型不是基本类型。

它还有助于防止错误,因为您的成员总是被初始化,而不是初始化取决于构造函数体内的控制流。