简体   繁体   中英

Calling a constructor of a member object within the body of the constructor?

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?

错误信息

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.

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).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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