Reputation: 826
Why am I not allowed to use the following syntax to call a constructor of a member object/different class in the body of the constructor of a class?
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;}
};
By the way I know I can call Circle::Circle(double)
via Cylinder(double,double)
using member initialization list like Cylinder(double r,double h) : base(r), height(r) {}
but still what's wrong with the former method that the compiler is generating this error?
Upvotes: 3
Views: 5497
Reputation: 40625
The correct way to handle this is an initializer list. Write the constructor of Cylinder
like this:
Cylinder(double r, double h) : base(r), height(h) {}
The part between the :
and the opening brace is the initializer list that constructs all the data members of the class before the body of the constructor code is run. That way, C++ ensures that your object is fully constructed even within the body of the constructor.
Since C++ ensures that all members are fully constructed before the body of the constructor is run, any attempt to call a constructor from the body of a constructor would reinitialize the object. While technically possible using placement-new, this is almost certainly not what you want to do.
The syntax base(r);
within the body of the constructor tries to call the operator()(double)
on the already fully constructed member. Since you didn't provide such an operator, your compiler complains.
Upvotes: 7
Reputation: 114491
The problem is that when C++ begins the execution of the constructor code all the member variables must have been already constructed (what if you for example call a method of Circle
before constructing it?).
If immediate construction is a problem then a possible solution is to add to your member a default constructor and the using assignment in the body of the constructor of the containing class.
You can imagine that native types like int
or double
do have a default constructor, that's why you can initialize them later (note however that for one of the many ugly quirks of the language the default constructor for an int
or a double
doesn't actually do anything in this case and you're not allowed to do anything with such a member except assigning it a value - for example reading it is not allowed).
You cannot use base(r)
in the body because that's not a valid statement... following a name with an opening parenthesis is only used for function call, for initialization in a declaration or for member initialization in the constructor member initialization list.
If you provide Circle
with a default constructor then you can do
Cylinder(double r, double h) {
base = Circle(r);
height = h;
}
Note however that the approach of constructing non-working objects to fix them later is not the best approach for C++. The language likes the idea that if an object is constructed then it's usable and deviations from this are considered only when necessary (C++11 drifted away a bit from this original path with move constructor... but that's another story).
Upvotes: 7