【问题标题】:Cannot access method of a child class无法访问子类的方法
【发布时间】:2021-10-18 09:45:03
【问题描述】:

我制作了一个我在代码中遇到的问题的玩具示例:

我有一只动物,我不知道后期会是什么,所以我将它初始化为普通动物。

但后来,我想让它变成一只猫,所以我将 myAnimal 指定为 Cat

#include <iostream>

class Animal {
public:
    int weight;
    virtual void Sound() {
        // To be implemented by child class
    }
};

class Cat : public Animal {
public:
    void Sound() {
        std::cout << "Miau" << std::endl;
    }

    // Only cats purr
    void Purr() {
        std::cout << "Purr" << std::endl;
    }
};

int main() {

    // At this point I don't know which animal I'll have, so I initialize it 
    // to a generic Animal
    Animal* myAnimal;
    animal->weight = 10;  


    // At this point of the code, I know what animal I want, so I assign animal 
    // to be a Cat
    double selectedAnimal = 0;      
    if (selectedAnimal == 0) {
        myAnimal = &Cat();
        // myAnimal = new Cat(); // this will just create a new Cat, losing 
        // the already assigned weight. 
        // I want to "upgrade" my generic animal, keeping its properties and adding 
        // new ones specific to cats
    }

    myAnimal->Sound();
    myAnimal->Purr();     // ERROR: Class Animal has no member Purr

    return 0;
}

我认为我没有正确地将myAnimal 分配为Cat,但它仍然是Animal。但是,当我执行 myAnimal = &amp;Cat(); 时,编译器不会抱怨。

所以我不明白编译器是否允许我将 Animal 分配给 Cat myAnimal = &amp;Cat(); 类,为什么当我尝试使用特定于类 Cat 的方法时它会抱怨。

我应该如何重新分配我的通用动物,使其成为具有所有方法的完整 Cat?

编辑:

回答一些cmets:

-Animal 不应该有 Purr 方法,只有猫 purr。

-我在编译时不知道我将拥有什么 Animal,这就是为什么我一开始就将它指定为泛型。

我可以将 myAnimal 重新分配为一只新的 Cat,但是任何已经设置为通用动物的变量都将丢失(例如:Animal 在知道它是一只猫之前可能已经设置了一个权重变量)

我会尝试@Some程序员老兄建议的向下转换

【问题讨论】:

  • myAnimal = &amp;Cat(); - 这应该做什么?那不应该是myAnimal = new Cat(); 吗?其次,Animal 类没有 Purr 方法的错误是不言自明的。看课程Animal。你看到 Purr 方法了吗?
  • Cat myCat; myAnimal = &amp;myCat 将是另一种选择 - 只要 cat 的范围足够长
  • myAnimal-&gt;Purr(); 动物不会发出咕噜声,只有猫会发出咕噜声。你有一个设计问题。
  • 编译器不会抱怨myAnimal = &amp;Cat();,因为它不是格式错误的(因此不需要诊断),它只是未定义的行为
  • 这不是 python,Animal* myAnimal 永远只有动物方法,无论你分配什么。 C++ 是静态类型语言。

标签: c++ class inheritance


【解决方案1】:

首先,在创建 Cat 时确保正确分配内存。目前,您正在获取一个临时对象的地址,即 &amp;Cat(),它不是有效的 C++。

您可以通过两种不同的方式做到这一点:

// On the stack
Cat cat;
myAnimal = &cat;

// OR
myAnimal = new Cat(); // On the heap (remember to free the cat)

然后,当您想将动物用作猫时,可以使用向下转换例如:

auto myCatPtr = dynamic_cast<Cat*>(myAnimal);
if (myCatPtr) {
    // This means the pointer is valid
    myCatPtr->Purr();
}

这是working example

【讨论】:

    【解决方案2】:

    Matthias Grün 的回答展示了如何操纵 C++ 来做你想做的事。 然而,我的建议是停止让 C++ 做你认为正确的事情,而是按照 C++ 想要做的方式去做。 C++ 希望你永远不要丢弃对象的类型。它是一种强类型语言。丢弃对象的类型几乎总是一种“反模式”。 避免这种反模式的一种常用技术是将“所有权”与“使用”分开。您可以轻松地使用指向未知类型的指针。通过指向未知类型的指针来拥有一个对象真的很难。

    int main()
    {
       Cat my_cat;
       Animal* any_animal = &my_cat; // non-owning pointer.
       any_animal->Sound();
       my_cat.Purr();
    }
    

    【讨论】:

      【解决方案3】:

      myAnimal = &amp;Cat(); 所做的只是创建一个 Cat 类型的临时变量并将其地址分配给 myAnimal,从而为您留下一个悬空指针。 myAnimal 之后会指向一个无效的地址。

      此外,即使它已被正确分配,例如通过书写

      myAnimal = new Cat{};
      

      它仍然需要强制转换,以便编译器知道它正在处理 Cat,例如:

      auto pCatInstance = dynamic_cast<Cat*>(myAnimal);
      if (pCatInstance != nullptr)
         pCatInstance->Purr();
      

      【讨论】:

      • 请注意,即使使用临时地址也有问题。这在 C++ 中是非法的:一元 &amp; 运算符的操作数应该是某种类型的左值 T
      猜你喜欢
      • 1970-01-01
      • 2016-05-20
      • 1970-01-01
      • 1970-01-01
      • 2019-09-09
      • 1970-01-01
      • 1970-01-01
      • 2015-03-12
      相关资源
      最近更新 更多