Calling a constructor of a member object within th

2020-07-18 20:04发布

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?

Error Message

2条回答
成全新的幸福
2楼-- · 2020-07-18 20:42

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

查看更多
一纸荒年 Trace。
3楼-- · 2020-07-18 20:50

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.

查看更多
登录 后发表回答