0

I am having trouble understanting how to differentiate between multiple instances of the base class in a multiple inheritance situation. When looking for solutions, i only found answers about virtual inheritance but that does not solve my problem at all, since i do not want to have a single instance of the base in the final class at the end of the inharitance tree.

On wikipedia, the first paragraph states that:

Without virtual inheritance, if two classes B and C inherit from a class A, and a class D inherits from both B and C, then D will contain two copies of A's member variables: one via B, and one via C. These will be accessible independently, using scope resolution.

but i did not find any info on how to use scope resolution in my situation.

As for my task, the (forced and overcomplicated) class hierarchy is:

class Vehicle
{
    protected:
        unsigned long uPrice;
        char* szOwner; // for some reason, not allowed to use std::string in my homework...
    public:
        unsigned long GetPrice();
        // ...
}

class Trailer : public Vehicle
{
    protected:
        unsigned long uWid;
        unsigned long uHei;
        unsigned long uLen;
        unsigned long uMaxWeight;
    public:
        // ...
};

class Car : public Vehicle
{
    protected:
        unsigned long uWid;
        unsigned long uHei;
        unsigned long uLen;
        char* szBrand;
    public:
        // ...
};

class Trailer_Car : public Trailer, public Car
{
    private:
        unsigned long uTotalMass;
    public:
        // ...
};

As i stated above, i want multiple instances of Vehicle in an instance of Trailer_Car (one for Car and one for Trailer). And this works completly fine for:

Trailer_Car c(/*...*/, 3500, 1200);
std::cout << c.Trailer_Car::Car::GetPrice() << "\n"; // prints 3500
std::cout << c.Trailer_Car::Trailer::GetPrice();  // prints 1200

However, in my code i have to sort an inhomogeneous array (which can contain any of the 4 classes) and casting a Trailer_Car into Vehicle results in error: 'Vehicle' is an ambiguous base of 'Trailer_Car'. Example:

Vehicle** Arr = new Vehicle*[N];
// ...

Arr[i] = static_cast<Vehicle*>(new Trailer_Car); // error: 'Vehicle' is an ambiguous base of 'Trailer_Car'

How can i solve this? I know the error comes from the fact that Arr[i] doesen't know to which Vehicle from Trailer_Car to point but still nothing C++ like comes to mind.
As i am more used to C i would just make Arr a void** altough i do not know how good of a practice that is in C++ and i am asking this to avoid doing C in C++.

Tudor
  • 436
  • 3
  • 10
  • Does this answer your question? [How does virtual inheritance solve the "diamond" (multiple inheritance) ambiguity?](https://stackoverflow.com/questions/2659116/how-does-virtual-inheritance-solve-the-diamond-multiple-inheritance-ambiguit) – drescherjm Dec 21 '21 at 15:31
  • Why do you not want to have a single instance of the base in the final class at the end of the inheritance tree? What does it mean to have `Trailer_Car` derive from 2 base `Vehicle` classes? – awakened Dec 21 '21 at 15:34
  • @drescherjm Sadly no, virtual inheritance will make a single instance of the base class, and i need two (one to store the price and owner for the trailer, one to store the price and owner for the car) – Tudor Dec 21 '21 at 15:34
  • 3
    You do realize that without a virtual destructor you won't be able to properly free any classes inheriting from `Vehicle`, given only a pointer to `Vehicle`. A more "natural" alternative to `Trailer_Car` would be a class `CombinedVehicle` that owns 2 (or even more) vehicles and uses those to determine the properties of the vehicle it represents (e.g. using the sum of the prices of the parts as sum, ect.). As for the access: you could upcast to the point where it's no longer ambiguous, but as said that's not a good idea `Arr[i] = static_cast(new Trailer_Car);` – fabian Dec 21 '21 at 15:35
  • @fabian Yes, i know that, i would never design classes like this, but homework is homework :( and i cant modify the classes however i like. – Tudor Dec 21 '21 at 15:36

1 Answers1

2

You can use an intermediate conversion of the pointer type to control which instance of the repeated base class you get

Arr[i] = upcast<Car*>(new Trailer_Car);

or

Arr[i] = upcast<Trailer*>(new Trailer_Car);

I recommend to avoid a "real cast" for something that should be an implicit conversion. It won't force the compiler to do stupid things and shut up, the way a cast often will.

template<typename S, typename T> S upcast(const T& t) { return t; }

This way either of these lines gives you an implicit conversion with explicitly specified destination type, followed by an implicit conversion with inferred destination type (Vehicle*).

Note that in the process of storing a pointer to one or the other of the two Vehicle subobjects, you are also selecting which data the sort logic will use.

Also, you shouldn't manage dynamic memory by hand using new and delete, but use a smart pointer. A library one or one you wrote... but follow the Single Responsibility Principle and don't put memory management in the same class as anything else.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • This is definetly a step forward however, if `Arr[i] = upcast(new Trailer_Car);` how am i supposed to safelly access the `Car*` part? All i have for sorting the array is the `Vehicle**` array (and i know what type there is at any given index in the array so that i know how to call `GetPrice`). Still, the only thing that comes to mind is to make `Arr` a `void**` and do the casts and call the destructors by hand. – Tudor Dec 21 '21 at 15:54
  • 1
    @Tudor: If you know you have a pointer to the `Vehicle` within the `Trailer` subobject of a `Trailer_Car`, you can use a sequence of conversions to get back. `static_cast(static_cast(Arr[i]))`. Note that this time you need a "real cast", the compiler doesn't keep enough information to know if this is safe. If you have any virtual member functions, you can use `dynamic_cast` instead of `static_cast` (still in two steps) and the compiler will check the runtime type of the actual object. – Ben Voigt Dec 21 '21 at 15:58
  • 1
    But downcasts are a huge code smell. Maybe you want an interface, which is a virtual base class with only pure member functions. `struct IHasTotalPrice { virtual unsigned long TotalPrice() = 0; }; unsigned long Vehicle::TotalPrice { return GetPrice(); } unsigned long Trailer_Car::TotalPrice() { return Car::GetPrice() + Trailer::GetPrice(); }` and now your array can contain `IHasTotalPrice*` and the sort algorithm won't need to do any downcasts. – Ben Voigt Dec 21 '21 at 16:01