为什么我们不能在没有基类指针或引用的情况下在C++中实现多态?

Zia*_*man 0 c++ oop compiler-theory



首先看一下下面的代码(在这个代码形状中是基类,而行是派生类)

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);
}
Run Code Online (Sandbox Code Playgroud)


当我们编译这个程序时,首先会调用shape的draw方法,然后程序终止.为什么它会终止?为什么我们不能在没有基类指针或引用的情况下实现多态性这是什么技术原因?如果我们试图用对象数组实现多态,那么编译器会做什么?请用例子以可理解的方式解释.我会非常感激的.

xto*_*ofl 5

第一:你混合了两个概念:多态,价值与参考语义.

运行时多态性

多态性有多种形式.根据您使用的运行时间,可以使用其他选项.

一种解释型语言(如Ruby,Python和JavaScript中,...),允许"鸭输入":如果一个对象仅仅一个调用的方法foo,你可以调用它.通常这些语言进行垃圾收集,因此指针与对象的概念不太相关.

C++有不同的观点:允许多态,但是更严格.实现一个公共基类(可能是抽象的)允许编译器检查代码的语义:这样编译器确保你真正意味着foo实现预期接口的方法,而不是foos的一些混乱.

这种多态性是通过使用virtual函数来实现的:一个指向函数的指针,它可以在实现中变化.首先调用者foo必须查找函数指针的值,然后跳转到该函数.

到目前为止,多态性.

遏制

现在为了遏制:如果你line用C++ 创建一个对象数组,这些对象就在内存中彼此相邻; 他们被价值所包含.将数组传递给函数时,被调用函数只能接收相同类型的数组.否则,sizeof(shape)进入阵列的一步,我们将最终在一个中间line.

为了解决这个问题,你可以通过引用包含对象' - 在C++中我们使用指针.

编译时多态性

但是还有另一种实现多态函数的方法:模板.您可以drawshapes使用模板参数编写函数,该参数说明您正在使用的对象类型:

template< typename tShape, size_t N > 
void drawshapes( tShape (&aShapes)[N] ) {
    for( tShape* shape=aShapes; shape != aShapes+N; ++shape ) {
        shape->draw();
    }
}
Run Code Online (Sandbox Code Playgroud)

(注意:有stl函数可以简化这一过程,但这超出了问题的范围.

std::for_each( shapes, shapes+10, mem_fun_ref( &shape::draw ) );
Run Code Online (Sandbox Code Playgroud)

)


Dav*_*eas 5

您提出问题并提供代码示例但由于其他原因而失败.从你的问题的措辞:

为什么多态性需要引用/指针?

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);
}
Run Code Online (Sandbox Code Playgroud)

当您使用按值传递语义(或在基本容器中存储派生元素)时,您将创建类型base元素类型的副本derived.这就是所谓的切片,因为它类似于你有一个derived对象而你只切割/切割base它的子对象这一事实.在示例中,call1不能使用dmain中的对象,而是使用临时类型base,并且base::f被调用.

call2方法中,您将传递base对象的引用.当编译器call2(d)在main中看到它将创建base对子对象的引用d并将其传递给该函数.该函数对base指向类型对象的类型的引用执行操作derived,并将调用derived::f.指针也是如此,当你base *进入一个derived对象时,对象仍然存在derived.

为什么我不能将一个derived指针容器传递给一个带有指针容器的函数base

_Clearly如果derivedbase,一个容器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!!!
}
Run Code Online (Sandbox Code Playgroud)

如果语言允许标记有错误的行,那么它将允许应用程序将非类型的元素插入derived*容器中,这将意味着很多麻烦......

但问题是关于价值类型的容器......

当您拥有值类型的容器时,元素将被复制到容器中.插入类型的元素derived进入类型的容器base将使类型的子对象的副本base的内derived对象.这是相同的切片比上面.除了这是一种语言限制之外,它有充分的理由,当你有一个容器的容器时base,你有空间来容纳base元素.您不能将较大的对象存储到同一容器中.否则,编译器甚至不知道为每个元素保留多少空间(如果我们稍后使用更大的类型扩展呢?).

在其他语言中,它似乎实际上是允许的(Java),但事实并非如此.唯一的变化是语法.当你使用String array[]Java时,实际上你正在编写相当于string *array[]C++的东西.所有非基本类型都是语言中的引用,并且您不添加*语法的事实并不意味着容器包含String的实例,容器包含字符串的引用,这些引用与c ++引用的关系比c ++引用更多.