【问题标题】:Polymorphism and virtual in C++C++中的多态和虚
【发布时间】:2019-12-11 09:58:37
【问题描述】:

我对 C++ 中的多态性感到困惑。我正在自己研究它,我了解它的主要特点。但我不明白为什么它有帮助。在学习多态性(关于 oop)之前,我学习了继承(这很有帮助,因为您可以在超类和子类中使用一个方法只编写一次)。现在我被多态性和virtual 关键字困住了。我不明白为什么它有帮助。请看下面的代码(这是一个关于 C++ 学院的练习(我将获得认证))。为什么我只能声明为“虚拟”功能?我在代码中添加了变量n1n2n3(公开),为什么我不能访问它们?我完全不理解多态性,我在 StackOverflow 上阅读了大量关于多态性的帖子,但好像我理解了 50% 的多态性。我注意到多态性在 python 中不太难理解,因为 python 没有数据类型,但我也想在 C++ 中理解它,以及它的可能用途。

#include <iostream>
using namespace std;
class Pet {
    protected:
    string Name;
    public:
    Pet(string n) { Name = n; }
    virtual void MakeSound(void) { cout << Name << " the Pet says: Shh! Shh!"  << endl; }
    int n1;
};
class Cat : public Pet {
    public:
    Cat(string n) : Pet(n) { }
    void MakeSound(void) { cout << Name << " the Cat says: Meow! Meow!" <<       endl; }
    int n2;
};
class Dog : public Pet {
    public:
    Dog(string n) : Pet(n) { }
    void MakeSound(void) { cout << Name << " the Dog says: Woof! Woof!" << endl; }
    int n3;
};
int main(void) {
    Pet* a_pet1, * a_pet2;
    Cat* a_cat;
    Dog* a_dog;

    a_pet1 = a_cat = new Cat("Kitty");
    a_pet2 = a_dog = new Dog("Doggie");

    a_pet1->MakeSound();
    a_cat->MakeSound();
    static_cast<Pet*>(a_cat)->MakeSound();
    a_pet2->MakeSound();
    a_dog->MakeSound();
    static_cast<Pet*>(a_dog)->MakeSound();
}

【问题讨论】:

  • 现在编写一个接收任何 Pet 的函数并调用它的 MakeSound 函数。假设您有 30 种不同的 Pet 类型,而不仅仅是 2 种。也许您缺少的部分是编写将采用指针或引用基类的函数。
  • Why do we need virtual functions in C++? 的可能重复 接受的答案非常深入,并附有一个关于 为什么 virtual 函数有用的示例。
  • 您在哪里尝试访问n1n2n3?您的散文表明您的问题可能集中在这三个方面,但您的代码大多忽略了它们。
  • 好的,谢谢@Fureeish。我只看到帖子。是的,我理解它,但我真的不知道如何在实际环境中使用它,如果我在小程序中使用它就可以了,但是其他......
  • 考虑捕获const std::exception&amp; 并输出其what() 返回值。使用virtual 函数,您无需知道异常的确切类型即可获得有意义的信息和结果。

标签: c++ inheritance polymorphism virtual


【解决方案1】:

多态的关键思想是有一个方法。 这个方法会有不同的实现,根据特定的情况调用特定的实现。

让我们考虑这个例子:

#include <iostream>
using namespace std;
class Polygon{

 protected:
  int numVertices;
  float *xCoord, *yCoord;

 public:
  void set(){
   cout<<"From Polygon"<< endl;
  }

};
class Rectangle : public Polygon{
public:

 void set(){
   cout<<"From Rectangle"<< endl;
}

class Triangle : public Polygon{
public:

 void set(){
   cout<<"From Triangle"<< endl;

 }
};
int main(){

 Polygon *poly;
 Rectangle rec;
 Triangle tri;

 poly = &rec;
 poly->set();
 poly = &tri;
 poly->set();
}

当您运行此代码时,您的输出如下:

From Polygon
From Polygon

让我们在基类 (Polygon) 中添加 virtualset() 。这是你得到的:

From Rectangle
From Triangle

如果我们在基类(Polygon)中创建了一个虚函数,并且它在派生类中被覆盖(在这种情况下,TriangleRectangle),那么我们不需要 virtual 关键字在派生类中,函数自动被视为派生类中的虚函数。

如果set() 不是虚拟的set() 将调用方法的基类版本,即使 poly 指向 Rect。 另一方面,set() 是虚拟的,它将从派生类调用实际方法。 (在这种情况下,rect-&gt;set() 将打印“From Rectangle”)。

这样做意味着在我不知道对象的特定类型的情况下,我可以使用virtualpolymorphism,它会在调用过程中使用正确的方法。

我希望这会有所帮助!

【讨论】:

    【解决方案2】:

    你说得对,要理解多态性的用途以及学习它时的作用并不容易。像您提到的常见示例并没有真正的帮助,它只是展示了这个概念,但缺乏真实的背景。

    理解和能够使用多态是编程中比较高级的话题。在遵循真正的面向对象编程(如 SOLID 和设计模式)时使用它。

    多态性的一个很好的现实世界例子是迭代器设计模式。你定义了一个基类,比如使用next() 之类的方法的列表,然后你可以有不同的派生类(用于不同类型的列表)都覆盖这个方法,它们实现它,这样你就可以相应地迭代该列表。

    您可能会看到它变得复杂,所以我无法在这里解释所有内容,但您会得到一个想法和一些指示。

    【讨论】:

    • "但缺乏真实背景" 是的!可能我不明白它,但我不知道我应该在何时何地使用它。不幸的是,我不知道如何获得这项技能。你认为我能做什么?
    • @Riccardo 我给了你指点,寻找迭代器设计模式并在其中看到多态性。另一种大量使用多态的设计模式是状态设计模式,但几乎所有的设计模式都使用它。
    【解决方案3】:

    也许举个例子可以提供帮助。考虑一个不同的main(),像这样:

    int main()
    {
        std::vector<std::unique_ptr<Pet>> menagerie;
        menagerie.push_back(std::make_unique<Dog>("Fido"));
        menagerie.push_back(std::make_unique<Cat>("Morris"));
        menagerie.push_back(std::make_unique<Cat>("Garfield"));
        menagerie.push_back(std::make_unique<Dog>("Rover"));
    
        for (auto&& pet : menagerie)
        {
            pet->MakeSound();
        }
    }
    

    我们这里有一堆宠物。我们可以用同样的方式处理它们,但它们会发出不同的声音。对每个特定类型的宠物调用 MakeSound 是正确的。这种用例很常见。

    Fido the Dog says: Woof! Woof!
    Morris the Cat says: Meow! Meow!
    Garfield the Cat says: Meow! Meow!
    Rover the Dog says: Woof! Woof!
    

    现在,尝试删除 virtual 关键字,他们都会说“嘘!嘘!”。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-20
      • 2019-11-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多