Reputation: 35
I recently started learning C++ and have been using cplusplus.com's tutorials to learn. I got to the classes section, and saw how to initialize members without typing it in the constructor's body. This seemed simple at first, but their example left me confused. Here it is:
// member initialization
#include <iostream>
using namespace std;
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;}
};
int main () {
Cylinder foo (10,20);
cout << "foo's volume: " << foo.volume() << '\n';
return 0;
}
The part that confuses me is the Cylinder class, specifically:
Cylinder(double r, double h) : base (r), height(h) {}
Earlier in the class, a member object called "base" of type Circle was declared. Then, in the cylinder constructor, this "base" object was set to "r", which is a double:
base (r), height(h) {}
My question is, how could the member "base" be initialized to a double, when it is of type "Circle"? Also, I tried doing what is (what I think is) essentially the same thing, but instead I initialized "base" in the constructor's body:
Cylinder(double r, double h)
{
base = r;
height = h;
}
This left me with the compilation error: "error: no matching function for call to 'Circle::Circle()'"
If someone could clear up this confusion for me, I would greatly appreciate it. Thanks.
Upvotes: 2
Views: 700
Reputation: 135
Cylinder(double r, double h) : base (r), height(h) {}
Code above constructs object using the initialization list, "base (r), height(h)", this is where the initialization of base really happens: constructor of base is called.
If you put base into body of constructor like this:
Cylinder(double r, double h)
{
base = r;
height = h;
}
as there is nothing in initialization list but initialization of base has to happen in this phase, compiler would call the no-parameter constructor of base to perform this. But you have not provided such a constructor but a constructor with one parameter, in this case compiler would not generate the constructor without parameter as well, so compiler is failed to find the constructor of base without parameter it is expecting and will raise error.
To solve this, you just need to provide a constructor without parameter for class Circle:
class Circle {
double radius;
public:
Circle(double r) : radius(r) { }
Circle():radius(1.0){} // just example
double area() {return radius*radius*3.14159265;}
};
Then you code would work. But you may need to note what happens for "base = r" in the constructor body of Cylinder:
Regarding to member "height" in Cylingder, as it is of primitive type, it usually OK to initialize it in initialization list, or leave it as it is in memory then assign it with a value you want in constructor body of Cylinder. But if you would like to let "height" have determined value before go to constructor body, you have to initialize it in initialization lit
Upvotes: 0
Reputation: 1851
Look at this link on member initialization list. Especially the following applies to your case:
1) Initializes the base or member named by class-or-identifier using direct initialization or, if expression-list is empty, value-initialization
and
Before the compound statement that forms the function body of the constructor begins executing, initialization of all direct bases, virtual bases, and non-static data members is finished. Member initializer list is the place where non-default initialization of these objects can be specified.
Specifically in your examples, base
of type Circle
is direct-initialized with a double
in the member initializer list. But if base
does not appear in the member initializer list, it is default-initialized. However, the compiler doesn't synthesize a default constructor for class Circle
because there is other user defined constructors. That is why you see the error: "error: no matching function for call to 'Circle::Circle()"
You can create the default constructor with = default
Circle() = default;
Upvotes: 0
Reputation: 172894
how could the member "base" be initialized to a double, when it is of type "Circle"?
Member initialization list will call the appropriate constructor for base
, and Circle
has a constructor which take a double
as parameter, so it works.
base = r;
does compile too, because r
can be implicitly converted to Circle
by the above constructor, and then operator=(const Circle&)
(generated by compiler implicitly) will be called. But without member initialization list base
need to be initialized by the default constructor first, which Circle
doesn't have. That's what error message says.
Upvotes: 2