Home > OS >  Use class as type in other class constructor
Use class as type in other class constructor

Time:04-06

i would like to use a class Point in an other class Rect.

class Point
{
    int x, y;
    public:
    Point (int px, int py){
        x = px;
        y = py;
    }
};

class Rect
{
    Point top_left; 
    Point bottom_right;
    public:
    Rect (Point p1, Point p2){
        top_left = p1;
        bottom_right = p2;
    }
};

error message is: "main.cpp:31:30: error: no matching function for call to ‘Rect::Point::Point()’". In my understanding the constructor method of the Rect class uses two parameters of type Point to instantiate a Rect object. I guess that i cannot use "Point" as type since it sounds to me as the compiler wants to call a function. The error message doesn't help me so i hope you will. Thanks for that in advance.

CodePudding user response:

Members are initialized before the body of the constructor runs. When you write:

Rect (Point p1, Point p2){
    top_left = p1;
    bottom_right = p2;
}

Then before the constructor is executed the members top_left and bottom_right are initialized. Because Point has no default constructor the members cannot be initialized.

To initialize members with a constructor you should use the member initializer list:

Rect (Point p1, Point p2) : top_left(p1), bottom_right(p2) { }

The error can be prevented also by providing default initializers for the members:

class Rect
{
    Point top_left{0,0}; 
    Point bottom_right{0,0};
    public:
    Rect (Point p1, Point p2){
        top_left = p1;
        bottom_right = p2;
    }
};

Or by providing a default constructor for Point. A default constructor is one that can be called without parameters. However, in any case you should prefer the member initializer list over assignment in the constructor body, because initialization assignment is more expensive than initialization only.

CodePudding user response:

The problem is that when a Rect object is created, the member variables are constructed and initialized before the Rect constructor body is executed.

Since there's no explicit initialization of the Point member variables, they will need to be default constructible, which they aren't because you don't have a default Point constructor.

There are a couple of possible possible solutions, of which the simplest one is:

  1. Create a Point default constructor. It doesn't have to do anything and can be compiler generated (but you must still tell the compiler to generate it):

    class Point
    {
    public:
        Point() = default;
        ...
    };
    

    While Point can now be default constructed, it will leave the x and y members uninitialized.

But I would rather recommend another solution:

  1. Create a Rect constructor initializer list to initialize the member variables of Rect:

    class Rect
    {
        Point top_left; 
        Point bottom_right;
    
    public:
        Rect(Point p1, Point p2)
            : top_left(p1), bottom_right(p2)
        {
            // Empty
        }
        ...
    };
    

With the second solution no Point default constructor is needed.


As for why there's no default constructor created for Point, it's because you have declared another constructor. That prohibits the compiler from generating its own default constructor (without being told to do so as in the first alternative).

CodePudding user response:

In your given example, the data members top_left and bottom_right are default initialized before the body of Rect::Rect (Point, Point) gets executed. This means in your example for the statements:

Point top_left; //this statement needs default ctor in your example because this data member is default initialized 
Point bottom_right;//this statement needs default ctor in your example because this data member is default initialized 

to work the default constructor Point::Point() is required.

But the problem is that since you've user-defined constructor for your class Point, the compiler will not synthesize the default ctor Point::Point().

There are 2 ways to solve this problem.

Solution 1

First is that you can add a default constructor for class Point as shown below:

class Point
{
    int x, y;
    public:
        Point (int px, int py){
            x = px;
            y = py;
        }
        //default constructor
        Point(): x(0), y(0){
        }
};

Working demo.

In the above modified code, i've added a default constructor that uses constructor initializer list to initialize the data members x and y.

Solution 2

We can also use constructor initializer list in the constructor Rect::Rect(Point, Point) to initialize the data members by passing arguments instead of default initializing them.

class Rect
{
    Point top_left; 
    Point bottom_right;
    public:
    //use constructor initializer list to intialize the data members by passing arguments instead of default initializing them
    Rect (Point p1, Point p2): top_left(p1), bottom_right(p2){
       
    }
};

Working demo.

  • Related