3

I'm trying to use the solution for java-like enums in C++.

My Problem is that I'm trying to use the enum as a member in another class. So first we start with the familiar Planet enum:

#ifndef PLANETS_H
#define PLANETS_H

class Planet {
  public:
    static const double G = 6.67300E-11;
    // Enum value DECLARATIONS - they are defined later
    static const Planet MERCURY;
    static const Planet VENUS;
    // ...

  private:
    double mass;   // in kilograms
    double radius; // in meters

  private:
    Planet(double mass, double radius) {
        this->mass = mass;
        this->radius = radius;
    }

  public:

    double surfaceGravity() {
        return G * mass / (radius * radius);
    }
};

// Enum value DEFINITIONS
// The initialization occurs in the scope of the class,
// so the private Planet constructor can be used.
const Planet Planet::MERCURY = Planet(3.303e+23, 2.4397e6);
const Planet Planet::VENUS = Planet(4.869e+24, 6.0518e6);

#endif // PLANETS_H

Then we have a SolarSystem object that takes Planet objects.

#ifndef SOLARSYSTEM_H
#define SOLARSYSTEM_H

#include "Planets.h"
class SolarSystem {
  public:
    SolarSystem(int distance, const Planet& planet) {
        this->distance = distance;
        this->planet = planet;
    }

  private:
    int distance;   // in kilometers
    Planet planet;

};


#endif // SOLARSYSTEM_H

Now if we try to compile this we get the following errors:

SolarSystem.h: In constructor 'SolarSystem::SolarSystem(int, const Planet&)':
SolarSystem.h:7:53: error: no matching function for call to 'Planet::Planet()'
SolarSystem.h:7:53: note: candidates are:
Planets.h:17:5: note: Planet::Planet(double, double)
Planets.h:17:5: note:   candidate expects 2 arguments, 0 provided
Planets.h:4:7: note: Planet::Planet(const Planet&)
Planets.h:4:7: note:   candidate expects 1 argument, 0 provided

The problem can be fixed by including an empty Planet() constructor.

I was wondering if that is the most appropriate fix or if there is a solution that does not involve an empty constructor.

Community
  • 1
  • 1
James Oltmans
  • 1,057
  • 13
  • 26

2 Answers2

3

You should make Planet planet a reference and initialize it in the initializer list. Otherwise, C++ tries to make a copy of an instance of the Planet - precisely the thing that you wanted to avoid.

class SolarSystem {
public:
    SolarSystem(int distance, const Planet& planet)
    :   distance(distance)
    ,   planet(planet) {
    }

private:
    int distance;   // in kilometers
    const Planet& planet;

};
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • OK, next problem. Let's say I have a setter in there: void setPlanet(const Planet& planet) { this->planet = planet; } I can't use the initializer syntax there. How do I handle that? – James Oltmans May 23 '12 at 23:59
  • @JamesOltmans If you need assignable values there, unfortunately, you will need to make `planet` a pointer. All variables in Java are "pointers", but in C++ you need to make it explicit. You could hide pointers behind member functions taking and returning references, though. – Sergey Kalinichenko May 24 '12 at 01:11
1

If you use c++11, you can also use the Java-like C++ enum method that I described (https://stackoverflow.com/a/29594977/558366), which is basically a wrapper class around an int variable and allows you to use Planet almost as if it is an ordinary enum type (it supports casting to and from int and has the same size as int), but still has member functions, such as planet.SurfaceGravity(). It would allow your solar system header to compile (though you can and should remove the reference of the Planet argument in the constructor):

class SolarSystem {
  public:
    SolarSystem(int distance, Planet planet) {
        this->distance = distance;
        this->planet = planet;
    }

  private:
    int distance;   // in kilometers
    Planet planet;
};
Community
  • 1
  • 1
bcmpinc
  • 3,202
  • 29
  • 36