C++ 在不处理指针时调用子类(虚拟)函数(后期绑定)

C++ 在不处理指针时调用子类(虚拟)函数(后期绑定),c++,pointers,inheritance,C++,Pointers,Inheritance,看看答案,他们表明虚拟函数允许后期绑定,这导致在降级时能够调用子类函数 为了完整性,我加入了有问题的代码 class Animal { public: virtual void eat() { std::cout << "I'm eating generic food."; } }; class Cat : public Animal { public: void eat() { std::cout << "I'm eating a ra

看看答案,他们表明虚拟函数允许后期绑定,这导致在降级时能够调用子类函数

为了完整性,我加入了有问题的代码

class Animal
{
 public:
   virtual void eat() { 
       std::cout << "I'm eating generic food.";
   }
};
class Cat : public Animal
{
public:
  void eat() { std::cout << "I'm eating a rat."; }
};
我们看到调用func会导致

Animal *animal = new Animal;
Cat *cat = new Cat;
func(animal); // outputs: "I'm eating generic food."
func(cat);    // outputs: "I'm eating a rat."
Animal animal;
Cat cat;
func2(animal); // outputs: "I'm eating generic food."
func2(cat);    // outputs: "I'm eating generic food."
调用func2时会导致

Animal *animal = new Animal;
Cat *cat = new Cat;
func(animal); // outputs: "I'm eating generic food."
func(cat);    // outputs: "I'm eating a rat."
Animal animal;
Cat cat;
func2(animal); // outputs: "I'm eating generic food."
func2(cat);    // outputs: "I'm eating generic food."
我的问题是:

我怎样才能使接收参数(不是指针)的函数使用重写的方法呢?换句话说,func2怎么会导致“我在吃老鼠”。此外,我想了解为什么会出现这种差异。我先谢谢你

我怎样才能使接收参数(不是指针)的函数使用重写的方法呢?换句话说,func2怎么会导致“我在吃老鼠”

您可以使用引用而不是指针

void func2(Animal& xyz) { xyz.eat(); }
然后,使用

Animal animal;
Cat cat;
func2(animal);
func2(cat); 
会像你希望的那样工作

此外,我想了解为什么会出现这种差异


这是由对象切片引起的。请参阅了解对象切片是什么,以及如何在代码中发挥作用。

< P> C++中的整个虚函数机制是基于选择实际函数来调用的,该函数是基于调用中使用的对象的动态(“实际”)类型调用的。在这个函数中

void func2(Animal xyz) { xyz.eat(); }
xyz
对象的类型是
Animal
。它被明确硬编码为
动物
。它的静态类型是
Animal
。其动态类型为
Animal
。它在各个方面都是动物。您自己要求编译器使用上述参数声明来实现这一点


这意味着
xyz.eat()调用将始终调用
动物::eat()
。这是没有办法的。没有办法让它调用
Cat::eat()
,因为这里没有
Cat
对象。通过将
Cat
作为实际参数传递,您只需要求编译器从该
Cat
生成
Animal xyz
,然后忽略原始
Cat
。(这通常被称为“切片”。)所有后续工作都是使用
动物xyz

完成的。原因是
func2
函数不使用传递给它的对象。该函数有自己的参数变量
Animal xyx
,该变量由
Animal
cat
变量构成。因此,在
func2
中,始终有一个基类对象要处理,因此该函数始终调用通用答案

void func2(Animal xyz)  // uses its own Animal xyz, created on call
{
    xyz.eat();          // uses a local xyz object of Animal class with its generic functions
}

Cat cat;
func2(cat);       // here a new Animal xyz is created from the cat
必须将实际对象的指针或引用作为参数传递给函数,以允许函数访问对象的特定重写虚拟函数。像这样:

void func2(Animal& xyz)   // uses a reference to the argument object
{
    xyz.eat();            // the actual argument is used with its overridden functions
}

Cat cat;
func2(cat);               // the cat is passed to the callee

可以使用值语义实现类。那么你就不需要指针或引用了。看肖恩父母的“”这可能会让你大吃一惊。观看演讲,了解他如何使用值语义来避免糟糕的数据共享,并实现一个令人印象深刻的多态撤销系统。下面是一个带注释的示例:

// A generic draw function that any type that streams to std::ostream can use
template <typename T>
void draw(const T& x, ostream& out, size_t position)
{ 
  out << string(position, ' ') << x << endl; 
}

// A class that can hold anything with value semantics
// -- note: no virtual functions and no inheritance!
class object_t {
    // ... see the talk for the details here ...
    // This is where the magic happens
};

// Define a vector of our object_t to be a document
using document_t = vector<object_t>;

// Overload the draw() function for document - just iterate
// through the vector and call draw() on each item in it
void draw(const document_t& x, ostream& out, size_t position)
{
    out << string(position, ' ') << "<document>" << endl;
    for (auto& e : x) draw(e, out, position + 2);
    out << string(position, ' ') << "</document>" << endl;
}

// Define my own class that the code above knows nothing 
// about and that doesn't inherit from object_t
class my_class_t {
    /* ... */
};

// Overload draw for it
void draw(const my_class_t&, ostream& out, size_t position)
{ out << string(position, ' ') << "my_class_t" << endl; }

// Use all this stuff
int main()
{
    document_t document; // just a vector!

    // Add some objects that don't inherit from object_t!
    document.emplace_back(0);
    document.emplace_back(string("Hello!"));

    // Show what we've got so far
    draw(document, cout, 0);

    // Add some more stuff
    document.emplace_back(document); // Add a whole copy of the current doc!
    document.emplace_back(my_class_t()); // Add an arbitrary type that doesn't inherit from object_t!

    draw(document, cout, 0);
}
//流式传输到std::ostream的任何类型都可以使用的通用绘图函数
模板
空心图纸(常数T&x、横截面和横截面、尺寸和位置)
{ 

谢谢你的回答。有没有可能以这种方式重新编写代码,这样就不需要引用了?我正在处理的是实现两个类,其中需要一般行为。@LeastSquaresWonder,要获得动态分派或延迟绑定(如你所说),你需要传递指针或引用。你无法获得如果你传递了一个对象,动态调度。阅读对象切片以了解原因。