2

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.

bigrig78
  • 23
  • 2

3 Answers3

2

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.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Ok, awesome thanks for the help. 1. The base class is constructed first, with parameters passed with the initializer list 2. The derived class construcor is called, but initializer list is used first. My problem for 1 and 2 is I have no default. 3. The constructor body is then called ... My follow up question is; do we even need the constructor body really ever in practice? Because the only way you get there is by having default classes or initializing the variables from the list, but then having to use member functions in the body to then do second handling of the data anyway? – bigrig78 Oct 20 '20 at 11:53
  • @bigrig78 For simple initializations then it can be done all in the initializer list, and the constructor body can be empty. – Some programmer dude Oct 20 '20 at 12:01
0

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

jkb
  • 2,376
  • 1
  • 9
  • 12
0

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

n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243