简体   繁体   English

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

[英]Why we can't implement polymorphism in C++ without base class pointer or reference?



First of all have a look at the following code (in this code shape is the base class and line is the derived class) 首先看下面的代码(在此代码中,形状是基类,而线是派生类)

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);
}


when we compile this program the draw method of shape would be called at first then program terminates. 当我们编译该程序时,将首先调用shape的draw方法,然后程序终止。 why it terminates ? 为什么终止? why we can not implement polymorphism without base class pointer or reference what is the technical reason for this? 为什么没有基类指针或引用就不能实现多态性的技术原因是什么? what compiler will do if we are trying to implement polymorphism with the array of objects ? 如果我们尝试使用对象数组实现多态,编译器将做什么? please explain in much understandable manner with examples. 请通过示例以易于理解的方式进行解释。 I will be very thankful. 我将非常感谢。

First: you're mixing two concepts: polymorphism, and value vs. reference semantics. 首先:您要混合两个概念:多态性和值与参考语义。

Runtime polymorphism 运行时多态

Polymorphism comes in many shapes. 多态性有多种形式。 Depending on the runtime you use, other options are available. 根据您使用的运行时,其他选项可用。

An interpreted language (like Ruby, Python, Javascript, ...) allows for 'duck typing': if an object merely has a method called foo , you can call it. 一种解释型语言(例如Ruby,Python,Javascript等)允许进行“鸭子类型”操作:如果对象仅具有一个称为foo的方法,则可以调用它。 Typically these languages do garbage collection, so the notion of pointers vs. objects isn't too relevant. 通常,这些语言会进行垃圾回收,因此指针与对象的概念不太相关。

C++ has a different viewpoint: polymorphism is allowed, but in a more strict way. C ++有不同的观点:允许多态,但是以更严格的方式。 Enforcing a common base class (which may be abstract) allows the compiler to check the semantics of your code: this way the compiler assures that you really meant the foo method which implements the intended interface, and not some mishmash of foo s. 强制使用一个通用的基类(可能是抽象的)可以使编译器检查您的代码的语义:这样,编译器可以确保您确实是实现了预期接口的foo方法,而不是foo的杂乱混搭。

This polymorphism is realized through the use of a virtual function: a pointer to a function, which may vary amongst implementations. 这种多态性是通过使用virtual函数实现的: virtual函数的指针在实现之间可能有所不同。 The caller of foo will first have to look up the value of the function pointer, and then jump to that function. foo的调用者首先必须查找函数指针的值,然后跳转到该函数。

So far for polymorphism. 到目前为止为多态。

Containment 遏制

Now for containment: if you create an array of line objects in C++, these objects are right next to each other in memory; 现在进行遏制:如果在C ++中创建line对象数组,则这些对象在内存中彼此相邻; they're contained by value . 它们被价值所包含。 When you pass the array to a function, the called function can only receive an array of the same type. 当您将数组传递给函数时,被调用的函数只能接收相同类型的数组。 Otherwise, taking a step of sizeof(shape) into the array we would end up in the mid of a line . 否则,在数组中采用sizeof(shape)的步骤,我们最终将在line的中间。

In order to fix that, you can contain the objects 'by reference' - in C++ we use pointers for that. 为了解决这个问题,您可以“按引用”包含对象-在C ++中,我们为此使用了指针。

Compile-time polymorphism 编译时多态

But there is another way to achieve polymorphic functions: templates. 但是还有另一种实现多态功能的方法:模板。 You can write your drawshapes function with a template argument that says which type of object you are using: 可以使用模板参数编写drawshapes函数,该参数说明您使用的对象类型:

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

(Note: there are stl functions to simplify this, but that's out of the scope of the question. (注意:有stl函数可以简化此操作,但这超出了问题的范围。

std::for_each( shapes, shapes+10, mem_fun_ref( &shape::draw ) );

)

You are asking a question and providing a code example that fails but for a different reason. 您在问一个问题,并提供了一个失败的代码示例,但原因有所不同。 From the wording of your question: 从您的问题的措辞:

Why are references/pointers required for polymorphism? 为什么多态性需要引用/指针?

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);
}

When you use pass-by-value semantics (or store derived elements in a base container) you are creating copies of type base of the elements of type derived . 当您使用按值传递语义(或将派生的元素存储在基本容器中)时,您将创建derived类型的元素的base类型的副本。 That is called slicing , as it resembles the fact that you have a derived object and you slice/cut only the base subobject from it. 这称为切片 ,因为它类似于以下事实:您有一个derived对象,并且仅从该对象中切片/切割base子对象。 In the example, call1 does not work from the object d in main, but rather with a temporary of type base , and base::f is called. 在示例中, call1在main中的对象d不起作用,而是使用base类型的临时类型,并调用了base::f

In the call2 method you are passing a reference to a base object. call2方法中,您正在传递对base对象的引用。 When the compiler sees call2(d) in main it will create a reference to the base subobject in d and pass it to the function. 当编译器在main中看到call2(d) ,它将在d创建对base子对象的引用,并将其传递给函数。 The function performs the operation on a reference of type base that points to an object of type derived , and will call derived::f . 该函数在类型为base的引用上执行该操作,该引用指向derived类型的对象,并将调用derived::f The same happens with pointers, when you get a base * into a derived object, the object is still derived . 同样的情况发生在指针上,当你得到一个base *derived对象,该对象是否仍然derived

Why can I not pass a container of derived pointers to a function taking a container of base pointers? 为什么我不能将derived指针的容器传递给采用base指针的容器的函数?

_Clearly if derived are base , a container of derived is a container of base . _Clearly如果derivedbase ,的容器derived 的容器base

No. Containers of derived are not containers of base . 不。 derived容器不是base容器。 That would break the type system. 那会破坏类型系统。 The simplest example of using a container of derived as container of base objects breaking the type system is below. 使用的容器的最简单的例子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!!!
}

If the line marked with error was allowed by the language, then it would allow the application to insert elements that are not of type derived* into the container, and that would mean lots of trouble... 如果语言允许使用标记为错误的行,那么它将允许应用程序将非derived*类型的元素插入容器,这将带来很多麻烦...

But the question was about containers of value types... 但是问题是关于值类型的容器...

When you have containers of value types, the elements get copied into the container. 当您具有值类型的容器时,元素将被复制到容器中。 Inserting an element of type derived into a container of type base will make a copy of the subobject of type base within the derived object. derived类型的元素插入到base类型的容器中,将在derived对象内复制base类型的子对象。 That is the same slicing than above. 与上面的切片相同。 Besides that being a language restriction, it is there for a good reason, when you have a container of base objects, you have space to hold just base elements. 除了语言限制之外,还有一个很好的理由:当您拥有一个base对象容器时,您就有空间仅容纳base元素。 You cannot store bigger objects into the same container. 您不能将较大的对象存储到同一容器中。 Else the compiler would not even know how much space to reserve for each element (what if we later extend with an even-bigger type?). 否则,编译器甚至不知道为每个元素保留多少空间(如果以后再使用更大的类型扩展该怎么办?)。

In other languages it may seem as this is actually allowed (Java), but it is not. 在其他语言中,这似乎实际上是允许的(Java),但事实并非如此。 The only change is in the syntax. 唯一的变化是语法。 When you have String array[] in Java you are actually writting the equivalent of string *array[] in C++. 当您在Java中使用String array[] ,实际上是在编写C ++中的string *array[] All non-primitive types are references in the language, and the fact that you do not add the * in the syntax does not mean that the container holds instances of String , containers hold references into Strings, that are more related to c++ pointers than c++ references. 所有非原始类型都是该语言中的引用,并且您没有在语法中添加*的事实并不意味着容器包含String的实例 ,容器将引用包含在String中,与c ++指针比c ++更为相关参考。

You cannot pass an array of line instead of an array of shape. 您不能传递线阵列而不是形状阵列。 You must use array of pointers. 您必须使用指针数组。 This happens because when the function tries to access the second member, it does *(sarray + sizeof(shape)) instead of *(sarray + sizeof(line)), that would be the correct way to access the second element of an array of line. 发生这种情况的原因是,当函数尝试访问第二个成员时,它执行*(sarray + sizeof(shape))而不是*(sarray + sizeof(line)),这是访问数组第二个元素的正确方法行。

You want something like this: 您想要这样的东西:

void drawshapes(shape *sarray[],int size)
{
   for(int i=0;i< size; i++)
      sarray[i]->draw();
}

main()
{
   shape *larray[10];
   larray[0] = new line(p1,p2);//assuming that we have a point class
   larray[1] = new line(p2,p3);
   ..........
   drawshapes(larray, 10);
   // clean-up memory
   ...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么不能在多态C ++中破坏类 - Why can't class be destructed in polymorphism C++ 获取指向基类c ++的指针或引用 - getting a pointer or reference to the base class c++ C ++将指针转换为对函数参数中基类指针的引用的指针 - C++ casting a pointer to a reference to pointer of base class in function parameter C ++静态多态性–引用专业模板方法,这些方法从基类指针的派生类中重载 - C++ Static Polymorphism––Referencing Specialized Template Methods Overloaded In Derived Class From Base Class Pointer C++ 删除类指针和多态性 - C++ delete a class pointer & polymorphism 为什么我不能同时实例化对基类的引用和对派生类的指针? - Why can't I instantiate a reference to a base class at the same time as a pointer to a derived class? C ++ Seg故障引用存储的基类指针 - C++ Seg fault on reference to stored base class pointer 多态 C++:基指针的字段不使用派生类构造函数给出的值 - Polymorphism C++: field of base pointer not using value given by derived class constructor 为什么我们不能为C ++中的抽象类创建对象? - Why can't we create objects for an abstract class in C++? 为什么派生类指针在没有强制转换的情况下不能指向基类对象? - Why can't a derived class pointer point to a base class object without casting?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM