为什么子类调用其父方法而不是它自己的方法?

时间:2014-01-04 20:39:10

标签: c++ class inheritance methods virtual

我自己学习c ++并且遇到了一些我没想到的行为。我不确定,但确实认为这不是Java在类似情况下会做的事情。为什么DogCat依赖于te父类实现并发出通用声音?

#include <iostream>

class Animal{
    public:
        virtual void speak(){std::cout << "???" << std::endl; /* not implemented */}
};

class Dog : public Animal{
    public:
        virtual void speak(){
            std::cout << "Woof" << std::endl;
        }
};

class Cat : public Animal{
    public:
        virtual void speak(){
            std::cout << "Meow" << std::endl;
        }
};

class Desk{
    /* Not an animal */
};

template<class T>
void please_talk(){
    T anim;
    Animal ani = anim;
    anim.speak();
    ani.speak();
}

int main()
{
    please_talk<Dog>();
    please_talk<Cat>();
    /* Does not compile please_talk<Desk>(); */
    return 0;
}

结果:

Woof
Generic animal sound
Meow
Generic animal sound

3 个答案:

答案 0 :(得分:5)

Animal ani = anim;

无论Animal的类型如何,都会创建anim类型的新对象。因此,ani.speak()会调用Animal::speak,因为这是类型Animal的覆盖。

如果要创建引用(或指针)

Animal & ani = anim;

然后将保留动态类型,并调用T::speak。这类似于复制对象引用时Java中发生的情况;但是你不应该尝试用Java来理解C ++对象模型,因为它们非常不同。

通过使基类抽象,可以防止意外创建基类对象(有时称为“切片”,因为它会切掉对象的派生类部分)。也就是说,使用默认实现声明函数pure virtual,而不是非pure:

virtual void speak() = 0; // Really not implemented

抽象类无法实例化为完整对象,因此如果您尝试这将导致编译时错误。

答案 1 :(得分:1)

此行Animal ani = anim;从特定动物创建一般动物。当你丢失原始派生类时,它被称为切片。

您需要将其更改为引用或指针:

Animal& ani = anim;

Animal* pAni = &anim;

答案 2 :(得分:0)

这都是因为你有Animal类的实际对象而不是指向动物类的指针或引用。因此,如果你想要调用dog和cat的speak()方法,那么就要做父类Animal的引用或指针。否则你会得到一个叫做方法的speak()方法,根据实际对象调用而不是多态中的引用。