我正在学习C ++形式Thinking in C ++ V1。我遇到了一个证明继承的例子。在这里
#include <iostream>
class Instrument{
public:
virtual void play(){
std::cout<<"instrument::play()";
}
};
class Wind: public Instrument{
public:
void play(){
std::cout<<"Wind::play()";
}
};
void tune(Instrument& i){
i.play();
}
int _tmain(int argc, _TCHAR* argv[])
{
Wind flute;
tune(flute);
return 0;
}
这会在控制台上输出Wind::play()
。
但如果我将方法'tune'改为
void tune(Instrument i){
i.play();
}
输出将instrument::play()
自'&amp;'以来添加以便传递长笛的参考而不是副本,为什么程序输出instrument::play()
而不是Wind::play()
?
答案 0 :(得分:17)
因为传递的副本的类型为Instrument
,而不是类型Wind
,因为Instrument
是该函数所采用的类型。这被称为“切片”。
答案 1 :(得分:7)
C ++中对象表达式的多态行为由其动态类型决定。
当将Instrument &i
类型的引用附加到Wind
类型的对象时,i
引用的对象的动态类型为保留。即即使使用i
类型声明Instrument
,它实际上指的是类型为Wind
的实际对象。这就是i
继续表现为Wind
的原因。如果使用指针引用某个对象,情况也是如此。
在正式语言中,这意味着表达式i
的静态类型是Instrument
,但是如果相同的表达式是{em>动态类型 {1}}。出于这个原因,Wind
的行为多态为i
。
同时,当您分配(或初始化)对象Wind
且值为Instrument i
时,您创建了一个类型为Wind
的独立独立对象i
。此对象的类型为Instrument
。因此,在所有多态上下文中,独立副本Instrument
将表现为i
。
再次,用正式语言说,这意味着在这种情况下,表达式Instrument
的静态类型和动态类型都是i
。出于这个原因,Instrument
的行为多态为i
。
在C ++语言中,“保留”对象的动态类型的唯一方法是使用指针或引用引用该对象。 “Refer”是关键词:您不创建新对象,而是引用已存在的对象。并且那些现有对象继续按照其原生类型行事。类型为Instrument
的对象继续表现为Wind
类型的对象。 (请注意,这仅适用于多态行为,例如,如何解析虚拟函数调用。)
但无论何时创建对象的独立副本(而不是将引用到原始文件),该副本将获得自己的生命,具有自己的类型和相关属性。类型为Wind
的独立副本与原始Instrument
对象无关。它没有记忆它作为Wind
对象的副本诞生的事实。它表现为Wind
。
答案 2 :(得分:3)
这是一个名为“slicing”的问题。将Wind
复制到Instrument
后,它会变为Instrument
。
答案 3 :(得分:0)
在
void tune(Instrument i){
i.play();
}
如果Instrument
对象和Instrument
对象将打印instrument::play
当您使用引用运算符&
时,您引用了Wind
类型的现有对象。
答案 4 :(得分:-2)
调用Instrument::play
函数的原因是,当您传递副本时,它实际上是您传递的对象的Instrument
部分,因为您slice该对象。 / p>