-1

I begin with C++ and I have trouble to use the polymorphism, especially when I want to use it in constructors of a new class. I know the issue has been treated here but I did not understand and my case is a little bit different.

To understand how to use it I built this little program. Here is the main.cpp :

#include <iostream>
#include <string>
#include <headers.h>
using namespace std;

void Display(Car &c)
{
    c.Show();
}

int main()
{
    Car Clio(50);
    SportCar Ferrari(200,200);
    Clio.Show();
    Ferrari.Show();

    Display(Ferrari);

    Race(Ferrari, Track());

    return 0;
}

The headers.h file :

#ifndef CAR_H
#define CAR_H

class Car
{
    public:
        Car(double s);
        Car();
        virtual void Show() const;

    protected:
        double m_Speed;

    private:
};

#endif // CAR_H


#ifndef SPORTCAR_H
#define SPORTCAR_H

class SportCar : public Car
{
    public:
        SportCar(double s, double p);
        SportCar();
        virtual void Show() const;

    protected:

    private:
        double m_Price;
};

#endif // SPORTCAR_H


#ifndef TRACK_H
#define SPORTCAR_H

class Track
{
    public:
        Track(double l);
        Track();

    protected:

    private:
        double m_Length;
};

#endif // SPORTCAR_H

#ifndef RACE_H
#define RACE_H


class Race
{
    public:
        Race(const Car& , Track t);

    protected:
        Car &m_Car;
        Track m_Track;

    private:
};

#endif // RACE_H

And finally, the src.cpp file :

#include <string>
#include <iostream>
#include <headers.h>
using namespace std;

Car::Car(double s)
{
     m_Speed = s;
}

Car::Car()
{
    m_Speed = 50;
}

void Car::Show() const
{
    cout << "I'm a car" << endl;
}

SportCar::SportCar(double s, double p) : Car(s)
{
    m_Price = p;
}

SportCar::SportCar() : Car()
{
    m_Price = 200;
}

void SportCar::Show() const
{
    cout << "I'm a sportcar" << endl;
}

Track::Track(double l)
{
    m_Length = l;
}

Track::Track()
{
    m_Length = 10;
}

Race::Race(const Car& c, Track t)
{
    m_Car = c;
    m_Track = t;
    m_Car.Show();
}

In summary I have two differents classes of car, one of which (SportCart) inherits from the other (Car) with two differents Show() methods. And I have a class Race whose members are a kind of car and a Track. When I use the Race constructor I can't pass in argument the car as a SportCar.

In result I have :

I'm a car
I'm a sportcar
I'm a sportcar
I'm a car

And I want :

I'm a car
I'm a sportcar
I'm a sportcar
I'm a sportcar

I try to use references like that :

protected:
    Car &m_Car;
    Track m_Track;

But it doesn't work either, I have this error I can't fix :

||=== Build: Debug in test (compiler: GNU GCC Compiler) ===|
E:\documents\c++\test\src\src.cpp||In constructor 'Race::Race(const Car&, Track)':|
E:\documents\c++\test\src\src.cpp|46|error: uninitialized reference member in 'class Car&' [-fpermissive]|
include\headers.h|66|note: 'Car& Race::m_Car' should be initialized|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

Thank you for your help.

2 Answers2

2

Note that in C++, "initialization" (creating an object in a certain way, like std::string str = "hi";) and "assignment" (changing the value of an existing object, like str = "hi";) are two different things.

When you write a constructor like

Car::Car(double s)
{
     m_Speed = s;
}

C++ needs the object and all its members exist, and so have already been initialized, by the time you reach the first {. The statement m_Speed = s; is an assignment, and not an initialization. Since you didn't specify how m_Speed should be initialized, C++ inserts logic to "default-initialize" it before the constructor body starts (although for a fundamental type like double, this just means it has an indeterminate value until assigned).

This doesn't usually matter for a member of type double. But it can for a member of class type, and it definitely does for a member of reference type: a reference MUST be initialized, because a reference must always refer to some object, and an assignment whose left-hand side is a reference variable always assigns to that referred object; there is no way to change what object a reference refers to.

The way to specify how a constructor initializes members is using a member initializer list:

Car::Car(double s)
    : m_Speed(s)
{}

Race::Race(const Car& c, Track t)
    : m_Car(c), m_Track(t)
{
    m_Car.Show();
}

Using member initializer lists whenever possible is a good habit, but here you ran into one case where it's absolutely required.

[Note 1: The Race constructor shown above still won't quite work because of another issue: you can't initialize a Car& reference from a const Car&. You'll need to either change the constructor to take a non-const parameter, or change the member to have type const Car&. A non-const parameter would also avoid potential issues with binding to a temporary.]

[Note 2: Another way to specify the initializer for class members is to put = something; after the member declaration within the class. This default initializer will be used when a constructor doesn't specify an initializer for that member and in an implicitly defined default constructor, if any. This can be convenient when you want multiple constructors to use the same initializer.]

aschepler
  • 70,891
  • 9
  • 107
  • 161
1

The issue is that a reference has to be initialized as the error message points out. To do this in a constructor we use an initializer list.

Race::Race(Car& c, Track t) : m_Car(c), m_Track(t)
{
    m_Car.Show();
}

Edit: m_Track does not need to initialized here, but it's good practice to always use the initializer list if possible, and it's also more efficient.

super
  • 12,335
  • 2
  • 19
  • 29
  • Why not also move `m_Track = t;` to the initialization list (`m_Track(t)`)? It would be more efficient. – Jesper Juhl Jan 19 '19 at 12:40
  • Ok I understand thank you, it works, but I have to remove the `const` to avoid the following error : `E:\documents\c++\test\src\src.cpp|46|error: invalid initialization of reference of type 'Car&' from expression of type 'const Car'|` –  Jan 19 '19 at 13:16
  • Ah, missed that. Updated. – super Jan 19 '19 at 13:29