首先看看下面的代码(这段代码中shape是基类,line是派生类)
void drawshapes(shape sarray[],int size)
{
for(int i=0;i< size; i++)
sarray[i].draw();
}
main()
{
line larray[10];
larray[0]=line(p1,p2);//assuming that we have a point class
larray[1]=line(p2,p3);
..........
drawshapes(larray,10);
}
当我们编译这个程序时,首先会调用 shape 的 draw 方法,然后程序终止。为什么它终止?为什么我们不能在没有基类指针或引用的情况下实现多态性这是什么技术原因?如果我们试图用对象数组实现多态性,编译器会做什么?请以易于理解的方式举例说明。我将非常感激。
最佳答案
您提出一个问题并提供了一个失败的代码示例,但原因不同。从你的问题的措辞来看:
为什么多态需要引用/指针?
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);
}
当您使用按值传递语义(或将派生元素存储在基础容器中)时,您正在创建 derived
类型元素的 base
类型拷贝。这称为切片,因为它类似于您有一个派生
对象并且您切片/剪切 仅基础
从它的子对象。在示例中,call1
不适用于 main 中的对象 d
,而是使用类型为 base
的临时对象,并且 base::f
被调用。
在 call2
方法中,您将传递对 base
对象的引用。当编译器在 main 中看到 call2(d)
时,它将在 d
中创建对 base
子对象的引用并将其传递给函数。该函数对指向类型derived
的对象的base
类型的引用执行操作,并将调用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
类型的容器中,将在 derived< 中复制
对象。这与上面的切片相同。除了作为一种语言限制之外,它还有一个很好的理由,当你有一个 base
类型的子对象base
对象的容器时,你有空间只容纳 base
元素。您不能将更大的对象存储到同一个容器中。否则编译器甚至不知道为每个元素保留多少空间(如果我们稍后扩展一个更大的类型会怎样?)。
在其他语言中,这似乎实际上是允许的(Java),但事实并非如此。唯一的变化是语法。当您在 Java 中使用 String array[]
时,您实际上是在编写 C++ 中的 string *array[]
的等价物。所有非基本类型在语言中都是引用,您不在语法中添加 *
并不意味着容器包含 String 的实例,容器将 引用 保存到字符串中,与 c++ 指针的关系比与 c++ 引用的关系更密切。
关于c++ - 为什么我们不能在没有基类指针或引用的情况下在 C++ 中实现多态性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2190455/