【问题标题】:Pass subclasses to a function that takes their superclass将子类传递给接受其超类的函数
【发布时间】:2012-12-02 19:41:16
【问题描述】:

假设我有三个课程 - AnimalCatDog,其中 CatDogAnimal 的子类(这听起来像第一堂课,但它不是我的作业承诺,只是简化真正的代码)

Dog* spike = new Dog(); 
Cat* puss = new Cat();   

int main(int argc, char** argv)
{
    function(spike, puss);
    return 0;
}

void function(Animal *pet, Animal *pet2)
{
   magic->andSoForth();
}

现在这会产生以下错误:

Cannot convert parameter 1 from 'Dog *' to 'Animal'
  No constructor could take the source type,
    or constructor overload resolution was ambiguous

将参数更改为完全匹配会产生类似的错误,只是它说它不能从一个类转换为同一个类。

我已经成功地调用了从超类继承的子类函数和成员,所以我知道这在逻辑上应该可以工作。我只是不知道这种语言要我以什么扭曲的方式扭曲逻辑。

编辑

解决方案恰好是:指针混淆了所有人。

  • 声明指针。
  • 将指针作为参数发送给不处理指针的函数。

在我的示例中,我将“非指针”发送到需要指针的函数,我只是切换了它。现在它工作正常。

【问题讨论】:

  • 检查编辑历史——第一个版本有Dog spike,但后来改为Dog* spike。一开始可能是不正确的,但是在问题中看到不正确的代码对于理解它与答案中正确代码之间的区别至关重要。谁能详细说明为什么会发生这种变化,以及基于它或问题的第一个版本给出的最佳答案?

标签: c++ inheritance parameters subclass


【解决方案1】:

当您动态分配一个新对象时,您会得到一个指向该对象的指针。所以你需要像这样将它存储在一个指针中:

Dog* spike = new Dog();
Cat* puss = new Cat();

然后您可以将spikepuss 传递给Animal* 类型的任何参数,假设DogCat 确实继承自Animal。这是 C++ 中多态的基础知识:

类型为“pointer to cv D”的纯右值,其中D是类类型,可以转换为类型为“pointer to cv”的纯右值> B”,其中BD 的基类(第10 条)。

当然,您可以立即将它们存储为Animal*

Animal* spike = new Dog();
Animal* puss = new Cat();

别忘了delete他们。更好的是,根本不要使用new

Dog spike;
Cat puss;
void function(const Animal&, const Animal&);
function(spike, puss);

【讨论】:

  • 我确实将它们声明为真实代码中的指针,对不起,我在做模型时很草率 :) 我尝试按照建议将它们更改为 Animal,但没有任何区别。还是一样的错误:/
  • @XistenZ 将实际代码和错误对应的行附加到您的问题中。
  • @XistenZ 您的编辑没有说明您做了什么来修复它。显示出了什么问题以及修复它的代码。另外,把答案放在你的答案中,而不是在问题中。 :) 谢谢
【解决方案2】:

合理假设您遇到的问题是将指针分配给非指针,反之亦然。但是您的代码不是真正的代码,而且您的错误消息显然不是真正的错误消息。所以这都是猜测,特别是那些已经发布的答案说“就是这样”(可能是,但不一定,不确定性完全是你自己的错)。


编辑:OP 在我发布此内容 10 秒后更改了问题的代码。

代码仍然与声称的错误消息不一致。

随着这个问题的变化,我不会追究它。


现在,该做什么了……


不要使用new

经验丰富的 C++ 程序员有时会以受控方式使用 new,并使用合适的代码进行包装。不称职的 C++ 程序员经常理所当然地使用new。但总的来说,你不需要它,而且它有问题,所以最好默认不要使用它。

然后,您的程序(您忽略显示)将如下所示:

#include <iostream>

struct Animal {};
struct Dog: Animal {};
struct Cat: Animal {};

void function(Animal const& pet1, Animal const& pet2 )
{
   //magicAndSoForth();
}

int main()
{
    Dog spike; 
    Cat puss;

    function( spike, puss );
}

【讨论】:

  • 我忽略了展示我的程序,因为我只是在遵循原则。我在创建不同的类时确实发送参数,你可以用结构来做吗?对它们了解不多,除了它们很像类,但更丑陋。
  • @XistenZ 但是更丑?我全神贯注(提示:“C++”!=“C#”,甚至不是string.Equals("C++", "C#", StringComparison.UniversalPolyglotIgnoreInterpunction)
  • 出于所有有意义的目的,它们与类完全相同。
  • @XistenZ 我不知道你的来源是什么,但请放心,结构和类一样丑陋。两者的唯一区别是,一个默认公开可见,另一个设置私有。
  • @XistenZ:在 C++ 中,结构 类。从字面上看,结构和类之间的唯一区别是默认可见性(结构的公共)和使用struct 而不是class 来声明类型。在其他所有方面,它们都是相同的。甚至继承、虚函数和所有经典的东西;同样,结构也可以做到所有这些。
【解决方案3】:

你的function 原型几乎可以肯定地说

void function(Animal pet1, Animal pet2);

或类似的东西。 (我知道你有一个原型,因为function 出现在main 之后。如果你没有前向声明它,C++ 会抱怨它根本找不到function,而不是它采用了错误的类型args。)

问题是,你真正的function 需要指针。由于main 出现在真正的function 之前,因此它看不到这一点。它只看到一个接受实际Animals 的声明,因此它尝试使用它......但失败了,因为 Animal 指针不是 Animal。 (与原型不同的真正 function 在 C++ 中很好,因为可能会重载。据编译器所知,function(Animal, Animal) 存在于另一个翻译单元中,而您也只是在定义 function(Animal*, Animal*)。)

查看您的代码以查找function 的声明,并说明

void function(Animal *pet1, Animal *pet2);

与实际函数的签名相匹配。

PS:如果您已包含所有相关声明,这将更容易弄清楚。

PPS: 一个更好的主意是使用引用,例如 suggested by Alf。但无论如何,为了做到这一点,您必须首先修复原型不匹配(或使真正的函数出现在使用它的代码之前)

【讨论】:

    【解决方案4】:

    嗯,你需要做的就是这个

    Animal *spike = new Dog();
    Animal *puss = new Cat();
    

    基本上,所有指针定义都必须属于基类,并且可以使用派生类指针进行初始化。

    【讨论】:

    • 你的意思是Animal *spike
    • 对不起,这是动物 * 斯派克,我的错误,我现在编辑了它。谢谢@Xymotech
    【解决方案5】:

    这就是它应该的样子。

    Dog* spike = new Dog();
    Cat* puss = new Cat();
    
    function(*spike, *puss);
    
    void function(Animal pet, Animal pet2)
    {
       //magic
    }
    

    经过测试并且可以正常工作。

    【讨论】:

    • 这个问题是它忘记了,例如,pet2 是一个Cat。在这种情况下,这没什么大不了的,因为动物实际上并没有做太多事情。但是稍后,当Cat 包含自定义行为和属性时,或者如果您想将一个松鼠放在向量或其他东西中,您将亲身体验“切片问题”。正确答案涉及找出指针版本中断的原因;它不应该像你想象的那么复杂。
    • 或者,声明要获取引用的函数。这比指针要好,但仍然不能解决您当前卡在按值传递 Animals 的原因。你需要弄清楚这一点;使用 C++,除非您通过指针或引用访问对象,否则不会获得多态性。没有多态性,子类就变得毫无用处。
    猜你喜欢
    • 1970-01-01
    • 2015-04-26
    • 1970-01-01
    • 2011-10-16
    • 1970-01-01
    • 2015-04-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多