c++ derived constructor when defining function outside of class body - "no default constructor"

Viewed 160

I'm working by myself through Bjarne Stroustrup's principles of programming book and am having trouble with defining constructors. This question requires using an interface which is built from the FLTK graphics library; generally no worries there. We have to make a smileyface out of the circle class, which does not have a default constructor - the regular constructor takes in a (x,y) point and a radius. I wanted to be able to make some calculations/do other stuff - so tried to define the constructor for my smileyface outside of the class body. This doesn't work, and I keep getting an error (from visual studio) of "no default constructor exists for class "Graph_lib::Circle".

struct Smiley :public Circle {
    Smiley(Point p, int rr);
    void draw_lines() const;
    void sc(Color c);

    Circle l_eye;
    Circle r_eye;
};

Smiley::Smiley(Point p, int rr) {
    Circle(p, rr);
    Circle l_eye(Point(p.x - 50, p.y - 50), rr / 8);
    Circle r_eye(Point(p.x + 50, p.y - 50), rr / 8);    
}

Basically, the above doesn't work. I think its trying to somehow create a default Circle for some reason, however cannot find the default function for it (as I haven't written one) and then throws the error. I've tried heaps of different combinations for this function but can't seem to get it to not try and fine a default. Note that if I get rid of the "Circle" type before l_eye and r_eye in the Smiley::Smiley - it still throws an error of "call of an object of a class type without appropriate operator() or conversion functions to pointer-to-function type".

However, if I just define it in the initialization list, it works fine.

struct Smiley :public Circle {
    Smiley(Point p, int rr) : Circle(p, rr), l_eye(Point(p.x - 50, p.y - 50), rr/8), r_eye(Point(p.x + 50, p.y - 50), rr/8) {}
    void draw_lines() const;
    void sc(Color c);

    Circle l_eye;
    Circle r_eye;
};

Obviously, this is a pretty trivial example. However, I think it's important for me to get this as there will be lots of times in the future where I will need to actually do computation in an outside constructor function. If anyone could help explain what the problem is that would be great.

3 Answers

The statement

Circle(p, rr);

creates a temporary Circle object, which isn't used.

Then

Circle l_eye(Point(p.x - 50, p.y - 50), rr / 8);
Circle r_eye(Point(p.x + 50, p.y - 50), rr / 8);    

are creating two new objects and variables, totally unrelated to your member variables of the same name.

The errors you get is because the compiler needs to initialize (construct) the base-class and the member-variables l_eye and r_eye, as part of the Smiley construction, and this initialization is done before the Smiley constructor function body is called.


In a way, your constructor:

Smiley::Smiley(Point p, int rr) {
    Circle(p, rr);
    Circle l_eye(Point(p.x - 50, p.y - 50), rr / 8);
    Circle r_eye(Point(p.x + 50, p.y - 50), rr / 8);    
}

is equivalent to:

Smiley::Smiley(Point p, int rr)
    : Circle(), l_eye(), r_eye()  // Default initialization
{
    Circle(p, rr);
    Circle l_eye(Point(p.x - 50, p.y - 50), rr / 8);
    Circle r_eye(Point(p.x + 50, p.y - 50), rr / 8);    
}

If Circle doesn't have a default-constructor (which it doesn't seem to have, or else you wouldn't be getting errors about it), then this is simply not possible.

You can write the initializer list in your out-of-class-definition constructor, just as you did with your in-class-definition constructor.

if I just define it in the initialization list, it works fine.

So do that. That's what you are supposed to do. Initialization lists exist exactly for doing that.

there will be lots of times in the future

You Ain't Gonna Need It.

do computation in an outside constructor function.

In a pinch you can write a helper function and copy-initialize your base class subobject from it:

Circle enormouslyComplicatedAuxiliaryFunction(Point p, int rr);

Smiley(Point p, int rr) : Circle(enormouslyComplicatedAuxiliaryFunction(p, rr)) { ... }

But if you find yourself doing it a lot, consider redesigning Circle. Construction is meant to be simple. If you need to compute lots of stuff to create your base class object, then you probably should have a base class constructor dedicated to computing that stuff.

(But wait, what if Circle is abstract? Well, if you need a lot of stuff to initialise an abstract class subobject, its design is almost surely broken.)

Related