【问题标题】:making code smaller for multiple inheritance使代码更小以实现多重继承
【发布时间】:2014-02-08 21:17:22
【问题描述】:

我编写了一个非常小的程序,您可以在其中输入您是男孩还是女孩,然后它会打印出一个声明。我的主要问题是,从我的代码中,除了从基类复制和粘贴之外,还有什么更简单的方法可以为女性编写代码。 这是我的代码

#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;
}

当我尝试从类Man 派生类Woman 时,它使person.getInfo(tName)person.showInfo() 模棱两可。这是为什么?以及如何使这段代码更小(针对女性)。

【问题讨论】:

  • 在深入了解多重继承之前,您应该认真学习单继承。提示:从另一个派生的类通常描述“是”关系。在您的示例中,人类类型的对象将同时是男人和女人,这没有什么意义。
  • 我知道@Timbo 的单一继承
  • 您不知道如何正确使用它。如果你是,你的 Human 将是 Man 和 Woman 的基类。只需忽略多重继承,直到您掌握了使用 C++ 进行的所有其他面向对象编程。您可能暂时不需要它(除了纯虚拟接口类)。
  • 好的,但是有没有解决这个代码的方法,让类 Woman 更短@Timbo
  • @Jim 是的,有。将Human 设为基类(不是派生类),并将通用功能放在那里。

标签: c++ class multiple-inheritance derived-class


【解决方案1】:

你把它弄反了——公共继承代表 IS-A 关系。所以你的用法是说“每个人都是男人,每个人都是女人。”这真的行不通。公共继承应该永远被用作“在一个地方获得所有功能”的便利。这就是组合(或者最坏的情况是非公共继承)的用途。

不过,您的情况是使用继承的一个很好的例子。它只需要遵循其自然定义(每个男人人;每个女人都是人)。也就是说,将Human 设为基类并在那里实现所有共享功能。像这样的:

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;
}

以上代码使用纯虚函数(必须在派生类中实现)来获取适当的性别字符串。其他所有内容对两种性别都是通用的,所以它在基类中。

请注意,有很多好的做法可以添加到代码中,但我不想过多地混淆这个问题。我不知道您是否可以访问 C++11 功能,所以我没有使用任何功能。将上述(工作)代码转变为好的代码的最佳选择是选择good C++ book

【讨论】:

    【解决方案2】:

    目前 Human 类确实代表两种性别,这在现实世界中是不正确的。它应该是相反的方式:一个 Human 类用作基础类,持有名称并提供可以在 Woman/Man 类中被覆盖的虚拟方法,因为它实际上是:成人:P 人类要么是男人或一个女人。目前的设计真的没有多大意义。

    另外,name 是男女通用的属性,和操作它的方法一样,所以应该去基类。你可以选择模板方法设计模式,所以最终你的代码可以这样改进:

    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) {}
    };
    

    然后你可以在对指向特定派生类的基类指针进行操作时轻松打印所需的属性:

    humanObject->showInfo();
    

    【讨论】:

    • 从 C++11 开始,通过 const 值返回是一个坏主意,它抑制了移动语义。
    • 顺便说一句。 @Angew,您的类确实错过了将名称作为输入参数或设置器的构造函数;您实际上不需要将 getGenderString 声明为受保护,因为运行时绑定也可以与私有方法一起正常工作,并且这里不需要虚拟 d-tor,因为它无法采取任何措施(尽管将来可能会更改)
    • 正如我所说,“有很多好的做法可以添加到代码中。”但是虚拟dtor 必要的-通过指向没有基类的指针删除派生类实例是未定义的行为,句号。它可能确实有效,但等待发生的是灾难。
    • 在这种情况下,虚拟 d-tor 绝对不需要:这些类没有做任何“花哨”的事情来提供一个,所以现在用不必要/空的方法污染代码比真正遵循必要性(当然,将来可能会改变,假设要扩展几行 - 然后,为了防止泄漏,虚拟 dtor 绝对是这种情况),但现在你不会通过保留额外的在 VTable 中引用。我确实认为 YAGNI 原则是一种提供清晰/可维护代码的有价值的方法。
    • 嗯,不提供虚拟 dtor 会使代码非常不可移植。 它依赖于了解编译器如何实现虚拟调用和虚拟销毁。从语言的角度来看,行为只是未定义的,这意味着任何事情都可能发生。请注意,例如,允许优化器假设 UB 不会发生并相应地生成代码。自愿提交 UB 以获得某种形式的代码“清晰性”绝不应该通过名副其实的任何代码审查。这与 YAGNI 无关。该标准明确规定,对于这种情况,您需要虚拟 dtor。
    猜你喜欢
    • 2012-06-02
    • 2012-04-26
    • 2016-10-02
    • 1970-01-01
    • 2020-07-01
    • 1970-01-01
    • 2017-06-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多