[英]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. 首先:您要混合两个概念:多态性和值与参考语义。
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. 到目前为止为多态。
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 ++中,我们为此使用了指针。
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如果
derived
是base
,的容器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.