为什么我不允许使用以下语法在类的构造函数体中调用成员对象/不同类的构造函数?
class Circle {
double radius;
public:
Circle(double r) : radius(r) { }
double area() {return radius*radius*3.14159265;}
};
class Cylinder {
Circle base;
double height;
public:
Cylinder(double r, double h) {
base(r);
height = h;
}
double volume() {return base.area() * height;}
};
顺便说一下,我知道我可以使用Circle::Circle(double)
之类的成员初始化列表通过Cylinder(double,double)
调用Cylinder(double r,double h) : base(r), height(r) {}
,但编译器生成此错误的前一种方法仍然有什么问题?
答案 0 :(得分:6)
处理此问题的正确方法是初始化列表。像这样编写Cylinder
的构造函数:
Cylinder(double r, double h) : base(r), height(h) {}
:
和左大括号之间的部分是初始化列表,它在构造函数体的运行之前构造类的所有数据成员。这样,C ++确保您的对象即使在构造函数的主体内也是完全构造的。
由于C ++确保在运行构造函数体之前完全构造所有成员,因此任何从构造函数体调用构造函数的尝试都会重新初始化该对象。虽然技术上可以使用placement-new,但这几乎肯定不是你想做的。
构造函数体内的语法base(r);
尝试在已完全构造的成员上调用operator()(double)
。由于您没有提供此类运算符,因此您的编译器会抱怨。
答案 1 :(得分:5)
问题在于,当C ++开始执行构造函数代码时,所有成员变量必须已经构造好(如果你在构造之前调用Circle
的方法会怎样?)。
如果立即构建是一个问题,那么可能的解决方案是在您的成员中添加默认构造函数,并在包含类的构造函数的主体中使用赋值
你可以想象像int
或double
这样的原生类型确实有一个默认的构造函数,这就是为什么你以后可以初始化它们的原因(但请注意,对于许多丑陋的怪癖之一)语言int
或double
的默认构造函数在这种情况下实际上并不执行任何 ,并且您不允许对此进行任何操作除了为其赋值之外的成员 - 例如,不允许读取它。)
您不能在正文中使用base(r)
,因为它不是有效的声明...在带有左括号的名称之后仅用于函数调用,用于声明中的初始化或用于成员初始化在构造函数成员初始化列表中。
如果您为Circle
提供默认构造函数,则可以执行
Cylinder(double r, double h) {
base = Circle(r);
height = h;
}
但是请注意,构建非工作对象以便以后修复它们的方法并不是C ++的最佳方法。该语言喜欢这样的想法:如果一个对象被构造,那么它是可用的,并且只有在必要时才会考虑与它的偏差(C ++ 11从移动构造函数的原始路径中移开了一点......但是那个&#39 ;另一个故事)。