3

Why cannot I privately derive a class from both a class and another class that the class being derived from uses as a base. So, I have the the following

class Money
{
public:
    void get() {std::cin >> amount;} 
private:
    float amount;
}

class Employee
{
public:
    void get() {std::cin >> lastName;} 
private:
    std::string lastName;
}

class Manager : public Employee, private Money  //money is used to store a wage
{
public:
    void get() {Employee::get(); Money::get();} 
}

class Executive : public Manager, private Money  // money is used to store additional bonuses
//warning :direct base 'Money' inaccessible in 'Executive' due to ambiguity  
{
public:
    void get() {Manager::get(); Money::get();}
}

I expected that in the Executive class Manager::get() would invoke Money::get() to save a wage, while the other Money::get() would save the additional bonuses.

As far as I know for now, private inheritance may be applied as a form of composition or when the base's interface should be hidden from outsiders, but is useful in implementing the class being derived. I undestand that in this particular case composition is all I need, still, I cannot understand what kind of ambiguity the compiler warns me about, for private inheritance does not allow neither outside classes nor subclasses of a privately derived class to take advantage of the relation

Sju Ton
  • 45
  • 4
  • Your posted code is incomplete - where is `Compensation` defined? – Dai Jun 23 '23 at 00:05
  • 10
    Prefer composition over inheritance. A person HAS money. Few people ARE money. – user4581301 Jun 23 '23 at 00:05
  • 3
    Also, from a software-design-perspective: [don't use inheritance to model things like peoples' roles](https://softwareengineering.stackexchange.com/questions/436738/why-is-inheritance-bad-in-a-person-student-model) - but better yet: [don't think you have to use inheritance just because it's available](https://boxbase.org/entries/2020/aug/3/case-against-oop/). – Dai Jun 23 '23 at 00:06
  • @Dai, sorry, I misspelt. The class Money was called Compensastion when I ran the code, but I thought I should have called it Money for clarity – Sju Ton Jun 23 '23 at 00:15
  • 2
    This has nothing to do with inheritance being `private`. Even if the access isn't private, your `Executive` class can't do operations on `Money` (e.g. call `Money::get()`) because it has inherited twice - there are two (effectively unnamed) instances of `Money` in the structure of an `Executive`, and it is ambiguous about which one is referred to when doing (say) `Money::get()`. There is no rule in the language that makes `Money::get(); Money::get();` refer to one base then another. – Peter Jun 23 '23 at 03:29
  • I see that `Employee` has a member called `lastName`. This is good. In exactly the same way `Manager` should have a member called `money`, inheritance is inappropriate here, irrespective of your technical difficulties. – john Jun 23 '23 at 06:21
  • In your world `Employees` don't have wages? – john Jun 23 '23 at 06:21
  • 1
    I ran into this situation, and Stroustrup told me to solve it like this: `struct ExecutiveMoney : Money {};` then `class Executive : public Manager, private ExecutiveMoney {...};`, with `void get() { Manager::get(); ExecutiveMoney::get(); }` ... ambiguity gone. – Eljay Jun 23 '23 at 11:30
  • @john, of course, they do, even in my world. But I was just experimenting with inheritance and wanted to know the rationale behind the limitation. The employees classes are just an example I came up with to ask the question so that it could be understood by those reading and trying to answer – Sju Ton Jun 23 '23 at 20:44
  • 1
    @SjuTon Well please ignore my poor attempt at humour. It seems that me and a few other responders missed the specific intent of your question. If you have an abstract question like yours it's arguably better to just use classes X, Y and Z rather than anything from the real world. – john Jun 24 '23 at 07:01

2 Answers2

4

When you inherit from a class in C++, it means that your class contains that base as a subclass (e.g. Money), with all of its members. When a derived class of that derived class inherits from Money again, it won't reuse that subclass, but get its own. What happens is:

class Manager : public Employee, private Money
// means structurally:
Manager {
    Employee { ... }
    Money { ... }
    ...
}

// and
class Executive : public Manager, private Money
// means structurally
Executive {
    Manager {
        Employee { ... }
        Money { ... }
        ...
    }
    Money { ... }
    ...
}

The problem is that Executive now has two instances of Money in its class hierarchy:

  • Executive::Money
  • Executive::Manager::Money

This is the ambiguity that the compiler is warning you about.

Solution A - Composition over inheritance

If you really think about, it makes no sense that a Manager inherits from Money, because that would imply that a Manager is an instance of Money. It would make a lot more sense if a Manager had Money:

class Manager {
protected:
    Money money;
public:
    ...
};

class Executive : public Manager { ... };

Because Manager::money is a protected data member, a derived class such as Executive will be able to use it.

Solution B - protected inheritance

DISCLAIMER: DON'T USE INHERITANCE HERE, IT'S CLEARLY WRONG. BUT IF YOU DESPERATELY INSISTED ON IT, HERE IS THE SOLUTION:

The most practical solution is to use protected inheritance instead. The outside world still won't have access to Money, but Executive will:

// protected inheritance makes Money available to derived classes
class Manager : public Employee, protected Money { ... };

// don't inherit Money again, use Manager::Money
class Executive : public Manager { ... };
Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
  • 1
    Given that the code in the `Manager` class only has access to `Manager::Money`, and the code in the `Executive` class only has access to `Executive::Money`, it seems like there is no requirement for ambiguity here; either class could reference `Money` and there is only one superclass it could possibly be referring to. Am I missing the underlying problem, or is the C++ standard just punting on implementing some disambiguation logic that could (in principle) be supported but currently isn't? – Jeremy Friesner Jun 23 '23 at 00:33
  • Thank you very much, you made it much more clear. But now I have another question. Assume I have made the inheritance protected and can access salary from Executive. But I want to add another Money subclass to Executive to hold bonuses. So there isn't a way to add bonuses to the Executive using inheritance one more time and all I can is to use composition? – Sju Ton Jun 23 '23 at 00:41
  • 1
    @SjuTon Inheritance is more limited than composition. You need to use the right tool for the situation. This is why everyone talks in terms of is-a relationships for inheritance and has-a relationships for composition. Many situations are not appropriate for inheritance, such as wanting to inherit from the same type multiple types. – TheUndeadFish Jun 23 '23 at 03:09
  • 1
    @SjuTon no, you cannot inherit from the same base class twice in the same derived class. It sounds like perhaps your `Money` class should be able to manage different accounts of money, or it should be called `Account` and then it would make sense for an `Executive` to have multiple accounts. – Jan Schultke Jun 23 '23 at 06:04
  • I don't think this answer should be even discussing the idea of inheriting from `Money` since that is clearly wrong, irrespective of the technicalities of how you might do it. – john Jun 23 '23 at 06:24
  • 1
    @john you have a point, but I still think the answer should contain it for those seeking an inheritance-based solution purely out of academic interest. I've swapped the solutions around and added a disclaimer. – Jan Schultke Jun 23 '23 at 06:39
1

I use "otherpart" to replace the Executive::get()::Money::get()
so don't need to worry about derive, if you just want to add a string

#include<iostream>

class Money
{
public:
    // void get() {std::cin >> amount;} 
    void out() {std::cout << amount <<" " << otherpart<<std::endl;};
    virtual void get() {std::cin >> amount >> otherpart;};
private:
    std::string amount;
    std::string otherpart;
};

class Employee
{
public:
    void get() {std::cin >> lastName;}
    void out() {std::cout << lastName<<std::endl;};
private:
    std::string lastName;
};

class Manager : public Employee, private Money  //money is used to store a wage
{
public:
    void get() {Employee::get(); Money::get();}
    void out() {Employee::out(); Money::out();}
};

class Executive : public Manager, private Money  // money is used to store additional bonuses
//warning :direct base 'Money' inaccessible in 'Executive' due to ambiguity
{
public:
    void get() {Manager::get();}
    void out() {Manager::out();}
};

int main(void) {
    Executive compen = Executive();
    compen.get();
    compen.out();
    return 0;
}
Jiu_Zou
  • 463
  • 1
  • 4