我试图理解多态,对象切片和指针在这段代码中是如何工作的。我在Visual Studio工作。
#include <iostream>
class Man {
public:
virtual void speak() { std::cout << "I'm a man." << "\n"; }
private:
};
class Soldier : public Man {
public:
virtual void speak() { std::cout << "I'm a soldier." << "\n"; }
private:
};
int main() {
Man man1;
Soldier soldier1;
man1 = soldier1;
std::cout << "Man1: "; man1.speak();
Man *man2 = new Man;
Man *soldier2 = new Soldier;
man2 = soldier2;
std::cout << "Man2: "; (*man2).speak();
Man *man3 = new Man;
Soldier *soldier3 = new Soldier; // "Man *soldier3 = new Soldier;" will give the same output.
*man3 = *soldier3;
std::cout << "Man3: "; man3->speak();
return 0;
}
输出结果为:
Man1: I'm a man.
Man2: I'm a soldier.
Man3: I'm a man.
我做了一些搜索并了解了这个概念&#34;对象切片&#34;。我猜这就是man1和man3发生的事情。但是等等,我们没有使用关键字&#34;虚拟&#34;在所有这些课程?不应该man1和* man3首先找出它们各自的类对象,并调用特定的覆盖说()?
或者是因为切片已经发生在=运算符处,在行:
man1 = soldier1;
这一行:
*man3 = *soldier3;
man1和* man3现在真的只是Man对象吗?
一位编码员猜测这是因为=运算符只是将右侧值分配给左侧变量的副本。它需要得到确认。
除此之外,我想要实现的目标是将Soldier对象复制到不同的内存地址,这与我在同一个内存地址指向两个指针的情况不同,对于man2和soldier2。
最后,我想知道为什么在第2部分中没有发生切片,以及在这样的语法中真正发生了什么:
Man *soldier2 = new Soldier;
说真的是做什么..?
我很欣赏这方面的任何见解。我是一名基本的C ++程序员:)&lt;
答案 0 :(得分:3)
我们没有使用关键字&#34; virtual&#34;在所有这些课程中
virtual
关键字与对象切片无关。
不应该man1和* man3首先找出它们各自的类对象,并调用特定的覆盖说()?
不,他们不能,他们不应该。 C ++有一定的对象模型。它包括占据特定内存区域的对象。在这个模型中,对象分配没有地方像你期望的那样工作。
考虑对士兵类的这种修改:
class Soldier : public Man {
public:
virtual void speak() { std::cout << "I'm a soldier with a" << weapon << "\n"; }
Soldier (const std::string& w) : weapon(w) {}
private:
std::string weapon;
};
现在,Soldier
个对象占用的空间比Man
个对象多。分配
man1 = soldier1
在man1
内根本没有适合武器弦的位置,所以它被切断了。现在士兵speak
无法工作,因为它无法找到武器,因此使用了人speak
。
当你指定指针时,没有切断,因为Man
指针完全能够指向Soldier
。在原始内存级别,所有指针基本相同,并且他们不关心要指向的内容。这就是多态可以发挥作用的原因。
有些人(包括我自己)会争辩说,由于对象切片几乎总是一个错误,因此尝试调用它应该会导致编译时错误而不是混淆静默破坏。但是这种语言目前还没有以这种方式定义,所以你必须要小心。
答案 1 :(得分:1)
或者是因为切片已经发生在=运算符处,在行:...
确实在man1 = soldier1处切片,但这并不影响man3 / soldier3的情况。在这种情况下切片与使用man3 / soldier3取消引用指针时完全相同。见下文。
当您说man2 = soldier2
时,您说&#34;将存储在指针man1中的地址设置为存储在soldier1&#34;中的地址。这不会导致地址切片,man1指向的内存位置现在是士兵。
但是,当你说*man3 = *soldier3
时,你告诉计算机将内存中man3的值分配给存储在soldier3中的值。换句话说,你说&#34;取存储在soldier3指向的内存位置的值,并将其存储在man3指向的内存位置。&#34; (顺便说一句,这被称为&#34;解除引用&#34;指针)。问题是存储一名男子所需的记忆太小而无法容纳一名士兵,因为士兵是一名男子加上一些其他数据。因此,在将新士兵数据存储在man内存位置之前,编译器将裁减。
最后,我想知道为什么在第2部分中没有发生切片,以及在这样的语法中真正发生了什么:
这不会导致切片的原因主要是因为C ++被设计为使用指针以这种方式工作。所有指针的实际大小在给定的体系结构上是相同的,并且编译器知道士兵&#34;是&#34;人。因为您将函数声明为虚拟,所以编译器知道对该函数使用正确的覆盖。
答案 2 :(得分:0)
用两个词:只有拥有Soldier类的实例时才会调用Soldier虚拟成员函数。您可以通过Man类指针或指向Soldier对象的引用来访问它。将Soldier变量赋值或复制到Man变量时,只复制基类Man,因为目标变量不能容纳Soldier类。