SlEePlEs5
SlEePlEs5

Reputation: 75

Strange bug with default constructor (C++)

First of all, I'd like to apologize in advance if the answer is obvious; I am very new to C++ and my first language is Java. I am also new to Stack Overflow, so if something is wrong with my question or you need anything else, please tell me.

So. I have this piece of code here: (I am using SFML for the vector and the CircleShape)

Ball::Ball() {

    // This ugly thing calls the full constructor with a random x and y position
    // in such a way the the entire ball is inside the screen.

    Ball::Ball((rand() % (WINDOW_X - (2 * BALL_RADIUS))) + BALL_RADIUS, (rand() % (WINDOW_Y - (2 * BALL_RADIUS))) + BALL_RADIUS);

}
Ball::Ball(float x, float y) {

    loc.x = x;
    loc.y = y;

    ball.setPosition(loc.x - BALL_RADIUS, loc.y - BALL_RADIUS);
    ball.setRadius(BALL_RADIUS);
    ball.setFillColor(sf::Color::Red);
    ball.setOutlineColor(sf::Color::Black);
    ball.setOutlineThickness(1);

}

And here is the header (#included into the above file):

class Ball {

private:
    sf::CircleShape ball;
    sf::Vector2f loc;
    sf::Vector2f vel;
    sf::Vector2f acc;

    void update();
    void bounce();
    void draw();

public:
    Ball();
    Ball(float x, float y);
    void run();

};

When I create the ball with

Ball ball;

(and yes, all of the SFML rendering stuff works), it never shows. A bit of investigation shows that its loc.x and loc.y variables are not set, and probably, neither are the radius, fillcolor, etc of the ball object. If I print the values of these with std::cout inside the constructor, loc.x and loc.y and all the others are set, so I assume that they get unset somewhere after the constructor. What is strange is that if I create the ball with

Ball ball((rand() % (WINDOW_X - (2 * BALL_RADIUS))) + BALL_RADIUS, (rand() % (WINDOW_Y - (2 * BALL_RADIUS))) + BALL_RADIUS);

or even

Ball ball(400, 300);

everything works perfectly, and the ball shows up on-screen. I am really stumped guys. If anyone could help me, that would be great.

BTW, I am running OS X 10.8 with Xcode 4.5.2, and using SFML RC2.0, if that makes any difference.

Thanks,

Matt

Upvotes: 5

Views: 486

Answers (7)

Open AI - Opting Out
Open AI - Opting Out

Reputation: 24153

Other answers give the syntactically correct way to do this.

I would make something semantically correct so that you call it as so:

Ball ball = Ball::createRandom();

You implement createRandom as a static function of Ball:

class Ball {
public:
    //...
    static Ball createRandom();
};

Implemented as:

int randomisePosition(int position) {
    return (rand() % (position - (2 * BALL_RADIUS))) + BALL_RADIUS;
}

Ball Ball::createRandom() {
    return Ball(randomisePosition(WINDOW_X),
                randomisePosition(WINDOW_Y));
}

Upvotes: 1

Roee Gavirel
Roee Gavirel

Reputation: 19453

Constructor Chaining is not supported in C++ before C++ 11

You can take the logic to a function and call it from both constructor. something like:

Ball::Ball() {

    // This ugly thing calls the full constructor with a random x and y position
    // in such a way the the entire ball is inside the screen.

   init((rand() % (WINDOW_X - (2 * BALL_RADIUS))) + BALL_RADIUS, (rand() % (WINDOW_Y - (2 * BALL_RADIUS))) + BALL_RADIUS);

}

Ball::Ball(float x, float y) {

    init(x,y);

}

Ball::init(float x, float y) {

    loc.x = x;
    loc.y = y;

    ball.setPosition(loc.x - BALL_RADIUS, loc.y - BALL_RADIUS);
    ball.setRadius(BALL_RADIUS);
    ball.setFillColor(sf::Color::Red);
    ball.setOutlineColor(sf::Color::Black);
    ball.setOutlineThickness(1);

}

Upvotes: 3

spiritwolfform
spiritwolfform

Reputation: 2293

You should make a init() method and call it in both of your constructors.

Ball::Ball((rand() % (WINDOW_X - (2 * BALL_RADIUS))) + BALL_RADIUS, (rand() % (WINDOW_Y - (2 * BALL_RADIUS))) + BALL_RADIUS);

creates a temp ball object and destroys it right away

Upvotes: 0

Joseph Mansfield
Joseph Mansfield

Reputation: 110728

Calling a constructor from another constructor (known as delegating a constructor) was impossible before C++11. To do it in C++11, you need to use the member initialisation list:

Ball::Ball()
 : Ball((rand() % (WINDOW_X - (2 * BALL_RADIUS))) + BALL_RADIUS,
        (rand() % (WINDOW_Y - (2 * BALL_RADIUS))) + BALL_RADIUS)
{ }

Pre-C++11, you can create another function that does the common work and get both constructors to call it.

Ball::Ball() {
  init((rand() % (WINDOW_X - (2 * BALL_RADIUS))) + BALL_RADIUS,
       (rand() % (WINDOW_Y - (2 * BALL_RADIUS))) + BALL_RADIUS);
}

Ball::Ball(float x, float y) {
  init(x, y);
}

void Ball::init(float x, float y) {
  loc.x = x;
  loc.y = y;

  ball.setPosition(loc.x - BALL_RADIUS, loc.y - BALL_RADIUS);
  ball.setRadius(BALL_RADIUS);
  ball.setFillColor(sf::Color::Red);
  ball.setOutlineColor(sf::Color::Black);
  ball.setOutlineThickness(1);
}

Upvotes: 7

Tony The Lion
Tony The Lion

Reputation: 63250

I would suggest instead of doing constructor chaining, that you use two-phase initialization, which would mean you create an init() function which you call in your default constructor.

Upvotes: 1

slugonamission
slugonamission

Reputation: 9642

Constructor chaining in C++ isn't allowed, instead, what will be happening here is that a temporary version of your class will be created, not assigned anywhere, then discarded.

Instead, make a private initialisation method with the parameters you want and call that from your constructors with the correct parameters.

Upvotes: 3

Aamir
Aamir

Reputation: 15576

Constructor Chaining is not supported in C++ unless you are using C++ 11 which I assume you are not.

Look at this answer for details:

LINK

Upvotes: 2

Related Questions