4

I have written a pretty small program where you type in whether you are a boy or a girl and it prints out a statement. My main question is that from my code is there any easier way of writing for the women beside copying and pasting from the base class. Here is my code

#include <iostream>
#include <string>


class Man{
protected:
    std::string name;
public:
    void getInfo(std::string hName){
        name = hName;
    }
    void showInfo(){
        std::cout << "Your name is: " << name << std::endl;
        std::cout << "And you are a MAN" << std::endl;
    }
};

class Women{ //is there an easier way to write this         
protected:
    std::string fem_name;
public:
    void getfemInfo(std::string fhName){
        fem_name = fhName;
    }
    void showfemaleInfo(){
        std::cout << "Your name is: " << fem_name << std::endl;
        std::cout << "And you are a Women" << std::endl;    
    }
};

class Human:public Man, public Women{
public:
    Human(){}
};

int main(){
    //local variables
    std::string choice;
    std::string tName;
    //declaring objects 
    Human person;
    //user interface
    std::cout << "Please enter you name: ";
    std::cin >> tName;
    std::cout << "Are you a [boy/girl]: ";
    std::cin >> choice;
    //if handler
    if (choice == "boy"){
        person.getInfo(tName);
        person.showInfo();
    }else if(choice == "girl"){
        person.getfemInfo(tName);
        person.showfemaleInfo();
    }
    system("pause");
    return 0;
}

When I try to derive class Woman from class Man, it makes person.getInfo(tName) and person.showInfo() ambiguous. Why is that? And how can I make this code smaller (for the women).

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
Jim
  • 171
  • 8
  • You should seriously learn single inheritance before dwelling into multiple inheritance. Hint: A class deriving from another usually describes an "is a"-relationship. In your example, an object of type Human would be a Man and a Woman at the same time, which makes little sense. – Timbo Feb 08 '14 at 21:24
  • Well I am aware of single inheritance @Timbo – Jim Feb 08 '14 at 21:27
  • 2
    You are not aware of how to use it properly. If you were, your Human would be the base class of Man and Woman. Just ignore multiple inheritance until you master all the rest of object oriented programming with C++. You will probably not need it for a while (apart from pure virtual interface classes). – Timbo Feb 08 '14 at 21:28
  • Alright but is there a solution to this code, to make the class Woman shorter @Timbo – Jim Feb 08 '14 at 21:35
  • @Jim Yes, there is. Make `Human` the base class (not the derived class), and put the common functionality there. – Angew is no longer proud of SO Feb 08 '14 at 21:42
  • If you don't mind can you show me an example because I am new to c++ @Angew – Jim Feb 08 '14 at 21:46

2 Answers2

3

You've got it backwards - public inheritance represents IS-A relationship. So your usage is saying that "every human is a man, and every human is a woman." That doesn't really work. Public inheritance should never be used as just a convenience for "getting all the functionality in one spot." That's what composition (or at worst, non-public inheritance) is for.

Your situation is a great example for employing inheritance, though. It just has to follow its natural definition (every man is a human; every woman is a human). That is, make Human the base class and implement all shared functionality there. Something like this:

class Human
{
protected:
  std::string name;

  virtual std::string getGenderString() const = 0;

public:
  virtual ~Human() {}  //dtor must be virtual to be usable as a polymorphic base class

  void getInfo(std::string hName)
  { name = hName; }

  void showInfo() const
  {
    std::cout << "Your name is: " << name << '\n';
    std::cout << "And you are a " << getGenderString() << std::endl;
  }
};


class Man : public Human
{
protected:
  virtual std::string getGenderString() const
  { return "MAN"; }
};


class Woman : public Human
{
protected:
  virtual std::string getGenderString() const
  { return "WOMAN"; }
};


int main(){
    //local variables
    std::string choice;
    std::string tName;
    //declaring objects 
    Human *person = NULL;
    //user interface
    std::cout << "Please enter you name: ";
    std::cin >> tName;
    std::cout << "Are you a [boy/girl]: ";
    std::cin >> choice;
    //if handler
    if (choice == "boy"){
        person = new Man();
    }else if(choice == "girl"){
        person = new Woman();
    }
    person->getInfo(tName);
    person->showInfo();
    system("pause");
    delete person;
    return 0;
}

The above code uses a pure virtual function (one which has to be implemented in a derived class) to get the appropriate gender string. Everything else is common to both genders, so it's in the base class.

Note that there's a lot of good practice which could be added to the code, but I didn't want to confuse the issue too much. I didn't know if you have access to C++11 features, so I didn't use any. Your best bet for turning the above (working) code into good code is picking up a good C++ book.

Community
  • 1
  • 1
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
0

At the moment Human class does represent both genders, which is not true in the real world. It should be rather the other way around: a Human class used as a base one, holding the name and delivering virtual methods which could be overridden in the Woman/Man classes, as it is in reality: adult :P human is either a man or a woman. The current design doesn't make much sense really.

Besides, name is a common property for both genders, same as the method operating on that, so they should go to the base class. You could go for template method design pattern, so eventually your code could be improved this way:

class Human
{
  std::string name;
  virtual std::string GetGender() const = 0;
public:
    // virtual d-tor

    Human(const std::string& humanName)
      : name(humanName) {}

    void showInfo() const 
    {
        std::cout << "Your name is: " << name << std::endl;
        std::cout << "And you are a " << GetGender() << std::endl;
    }
};

class Man : public Human{
  /*virtual*/ std::string GetGender() const { return "MAN"; }
public:
  Man(const std::string& name)
    : Human(name) {}
};

class Woman : public Human{
  /*virtual*/ std::string GetGender() const { return "WOMAN"; }
public:
  Woman(const std::string& name)
    : Human(name) {}
};

Then you could easily print the required properties when operating on the base class pointer pointing to the specific derived class:

humanObject->showInfo();
AdR
  • 115
  • 7
  • Returning by const value is a bad idea since C++11, it inhibits move semantics. – Angew is no longer proud of SO Feb 08 '14 at 22:04
  • btw. @Angew, your class does miss a constructor taking name as an input parameter, or a setter; you don't really need to declare getGenderString as protected, as runtime binding will properly work with private methods as well and virtual d-tor is not necessary here as there is no action it could take(it may be changed in the future though) – AdR Feb 08 '14 at 23:36
  • As I said, "there's a lot of good practice which could be added to the code." But the virtual dtor *is* necessary - deleting a derived class instance through a pointer to base class which doesn't have one is Undefined Behaviour, period. It might actually work, but it's disaster waiting to happen. – Angew is no longer proud of SO Feb 09 '14 at 09:49
  • In this case virtual d-tor is absolutely not needed: the classes do not do anything 'fancy' enough to provide one, so right now it is more polluting the code with unnecessary/empty methods, than really following the neccesity (that may change in the future of course, asuming that few lines are gonna be extended-then, to prevent the leaks, virtual dtor is absolutely the case), but for now you will not gain anything by keeping the additional reference in the VTable. I do consider YAGNI principle a valuable approach in providing clear/maintainable code. – AdR Feb 09 '14 at 16:21
  • Umm, not providing the virtual dtor makes the code *very non-portable.* It relies on knowing how the compiler implements virtual calls and virtual destruction. From the language point of view, the behaviour is simply undefined, which means literally *anything* can happen. Note, for example, that optimisers are allowed to assume UB does not happen and generate code accordingly. Willingly comitting UB to gain some form of "clarity" of code should never pass *any* code review worthy of the name. It's not about YAGNI; the standard clearly states that for such cases, you *need* the virtual dtor. – Angew is no longer proud of SO Feb 09 '14 at 18:11