您正在提出一个问题并提供一个失败但出于不同原因的代码示例。从你的问题的措辞:
为什么多态性需要引用/指针?
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
子对象的副本。这与上面的切片相同。除了语言限制之外,还有一个很好的理由,当你有一个对象容器时,你就有空间来容纳元素。您不能将较大的对象存储到同一个容器中。否则编译器甚至不知道为每个元素保留多少空间(如果我们以后扩展一个更大的类型呢?)。base
derived
base
base
在其他语言中,这似乎实际上是允许的(Java),但事实并非如此。唯一的变化是语法。当您使用Java 时,您实际上是在用 C++String array[]
编写等价物。string *array[]
所有非原始类型都是语言中的引用,并且您没有*
在语法中添加的事实并不意味着容器保存String 的实例,容器保存对字符串的引用,这些引用与 c++ 指针的关系比 c++ 引用更相关.