您正在提出一个问题并提供一个失败但出于不同原因的代码示例。从你的问题的措辞:
为什么多态性需要引用/指针?
struct base {
virtual void f();
};
struct derived : public base {
virtual void f();
};
void call1( base b ) {
b.f(); // base::f
}
void call2( base &b ) {
b.f(); // derived::f
}
int main() {
derived d;
call1(d);
call2(d);
}
base当您使用按值传递语义(或将派生元素存储在基本容器中)时,您正在创建 type元素的 type副本derived。这称为slicing,因为它类似于您有一个derived对象并且您只从中切片/剪切base子对象的事实。在示例中,call1不适d用于 main 中的对象,而是使用临时类型base, 并被base::f调用。
在该call2方法中,您传递对base对象的引用。当编译器call2(d)在 main 中看到时,它将创建base对子对象的引用d并将其传递给函数。base该函数对指向类型对象的类型引用执行操作derived,并将调用derived::f. 指针也是如此,当你base *进入一个derived对象时,对象仍然是derived.
为什么我不能将指针容器传递给采用derived指针容器的函数base?
_很明显,如果derived是base,一个容器derived 就是一个容器base。
不。 的容器derived不是 的容器base。那会破坏类型系统。下面是使用容器derived作为base破坏类型系统的对象容器的最简单示例。
void f( std::vector<base*> & v )
{
v.push_back( new base );
v.push_back( new another_derived );
}
int main() {
std::vector<derived*> v;
f( v ); // error!!!
}
如果语言允许标记有错误的行,那么它将允许应用程序将非类型元素插入derived*容器中,这将意味着很多麻烦......
但问题是关于值类型的容器......
当你有值类型的容器时,元素会被复制到容器中。将一个类型的元素插入一个类型derived的容器中,会在对象中创建一个类型的base子对象的副本。这与上面的切片相同。除了语言限制之外,还有一个很好的理由,当你有一个对象容器时,你就有空间来容纳元素。您不能将较大的对象存储到同一个容器中。否则编译器甚至不知道为每个元素保留多少空间(如果我们以后扩展一个更大的类型呢?)。basederivedbasebase
在其他语言中,这似乎实际上是允许的(Java),但事实并非如此。唯一的变化是语法。当您使用Java 时,您实际上是在用 C++String array[]编写等价物。string *array[]所有非原始类型都是语言中的引用,并且您没有*在语法中添加的事实并不意味着容器保存String 的实例,容器保存对字符串的引用,这些引用与 c++ 指针的关系比 c++ 引用更相关.