我完全糊涂在哪里放构造函数的定义。有时你会看到类似这样的东西
// 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
文件中实现,有时它们会在标题中定义,依此类推。
但在哪种情况下呢?哪个是好习惯,哪个不是?
答案 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)
除非
,否则应将所有函数实现保留在头文件之外一个干净的头文件非常有用,可以快速了解一个类的功能以及你应该如何使用它。
答案 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
,总是使用初始化列表来初始化成员是一个好习惯。这是因为如果在构造函数体中使用赋值,则最终调用该成员的默认构造函数,然后为其分配内容。这可能比仅仅使用适当的值初始化它更昂贵,特别是如果类型不是基本类型。
它还有助于防止错误,因为您的成员总是被初始化,而不是初始化取决于构造函数体内的控制流。