从派生**到基础**的转换

发布于 2024-12-13 19:33:52 字数 1475 浏览 0 评论 0原文

我正在阅读,不幸的是无法理解深入了解为什么编译器不允许从 Derived** 到 Base** 的转换。我还看到 this 没有给出比 parashift.com 的链接有更多信息。

编辑:

让我们逐行分析这段代码:

   Car   car;
   Car*  carPtr = &car;
   Car** carPtrPtr = &carPtr;
   //MyComment: Until now there is no problem!

   Vehicle** vehiclePtrPtr = carPtrPtr;  // This is an error in C++
   //MyComment: Here compiler gives me an error! And I try to understand why. 
   //MyComment: Let us consider that it was allowed. So what?? Let's go ahead!

   NuclearSubmarine  sub;
   NuclearSubmarine* subPtr = ⊂
   //MyComment: this two line are OK too!

   *vehiclePtrPtr = subPtr;

   //MyComment: the important part comes here... *vehiclePtrPtr is a pointer to
   //MyComment: a vehicle, particularly in our case it points to a Car object.
   //MyComment: Now when I assign to the pointer to the Car object *vehiclePtrPtr,
   //MyComment: a pointer to NuclearSubmarine, then it should just point to the
   //MyComment: NuclearSubmarine object as it is indeed a pointer to a Vehicle,
   //MyComment: isn't it? Where is my fault? Where I am wrong?

   // This last line would have caused carPtr to point to sub!
   carPtr->openGasCap();  // This might call fireNuclearMissle()!

I was reading this and unfortunately could not understand in depth why the compiler does not allow conversion from Derived** to Base**. Also I have seen this which gives no more info than the parashift.com's link.

EDIT:

Let us analyze this code line by line:

   Car   car;
   Car*  carPtr = &car;
   Car** carPtrPtr = &carPtr;
   //MyComment: Until now there is no problem!

   Vehicle** vehiclePtrPtr = carPtrPtr;  // This is an error in C++
   //MyComment: Here compiler gives me an error! And I try to understand why. 
   //MyComment: Let us consider that it was allowed. So what?? Let's go ahead!

   NuclearSubmarine  sub;
   NuclearSubmarine* subPtr = ⊂
   //MyComment: this two line are OK too!

   *vehiclePtrPtr = subPtr;

   //MyComment: the important part comes here... *vehiclePtrPtr is a pointer to
   //MyComment: a vehicle, particularly in our case it points to a Car object.
   //MyComment: Now when I assign to the pointer to the Car object *vehiclePtrPtr,
   //MyComment: a pointer to NuclearSubmarine, then it should just point to the
   //MyComment: NuclearSubmarine object as it is indeed a pointer to a Vehicle,
   //MyComment: isn't it? Where is my fault? Where I am wrong?

   // This last line would have caused carPtr to point to sub!
   carPtr->openGasCap();  // This might call fireNuclearMissle()!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

森末i 2024-12-20 19:33:55

这基本上和一碗香蕉不是一碗水果的原因是一样的。如果一碗香蕉是一碗水果,你可以把一个苹果放进碗里,它就不再是一碗香蕉了。

只要您只检查碗,这种转换是无害的。但一旦您开始修改它,转换就会变得不安全。这是要牢记的关键点。 (这就是为什么不可变的 Scala 集合实际上允许转换,但可变集合禁止转换的确切原因。)

与您的示例相同。如果存在从 Derived**Base** 的转换,则您可以放置​​一个指向苹果的指针,而类型系统承诺只能存在指向香蕉的指针。繁荣!

It's basically the same reason why a bowl of bananas is not a bowl of fruits. If a bowl of bananas were a bowl of fruits, you could put an apple into the bowl, and it would no longer be a bowl of bananas.

As long as you only inspect the bowl, the conversion is harmless. But as soon as you start modifying it, the conversion becomes unsafe. This is the key point to bear in mind. (This is the precise reason why the immutable Scala collections actually allow the conversion, but the mutable collections prohibit it.)

Same with your example. If there was a conversion from Derived** to Base**, you could put a pointer to an apple were the type system promised only a pointer to a banana could exist. Boom!

酒几许 2024-12-20 19:33:55

这将允许不乏无意义的错误:

class Flutist : public Musician
...

class Pianist : public Musician
...

void VeryBad(Flutist **f, Pianist **p)
{
 Musician **m1=f;
 Musician **m2=p;
 *m1=*m2; // Oh no! **f is supposed to be a Flutist and it's a Pianist!
}

这是一个完整的工作示例:

#include <stdio.h>

class Musician
{
 public:
 Musician(void) { ; }
 virtual void Play(void)=0;
};

class Pianist : public Musician
{
 public:
 Pianist(void) { ; }
 virtual void Play(void) { printf("The piano blares\n"); }
};

class Flutist : public Musician
{
 public:
 Flutist(void) { ; }
 virtual void Play(void) { printf("The flute sounds.\n"); }
};

void VeryBad(Flutist **f, Pianist **p)
{
 Musician **m1=f;
 Musician **m2=p;
 *m1=*m2; // Oh no! **f is supposed to be a Flutist and it's a Pianist!
}

int main(void)
{
 Flutist *f=new Flutist();
 Pianist *p=new Pianist();
 VeryBad(&f, &p);
 printf("Mom is asleep, but flute playing wont bother her.\n");
 f->Play(); // Since f is a Flutist* this can't possibly play piano, can it?
}

这里是它的实际应用:

$ g++ -fpermissive verybad.cpp -o verybad
verybad.cpp: In function void VeryBad(Flutist**, Pianist**):
verybad.cpp:26:20: warning: invalid conversion from Flutist** to Musician** [-fpermissive]
verybad.cpp:27:20: warning: invalid conversion from Pianist** to Musician** [-fpermissive]
$ ./verybad 
Mom is asleep, but flute playing wont bother her.
The piano blares

There are no shortage of senseless errors this would permit:

class Flutist : public Musician
...

class Pianist : public Musician
...

void VeryBad(Flutist **f, Pianist **p)
{
 Musician **m1=f;
 Musician **m2=p;
 *m1=*m2; // Oh no! **f is supposed to be a Flutist and it's a Pianist!
}

Here is a full working example:

#include <stdio.h>

class Musician
{
 public:
 Musician(void) { ; }
 virtual void Play(void)=0;
};

class Pianist : public Musician
{
 public:
 Pianist(void) { ; }
 virtual void Play(void) { printf("The piano blares\n"); }
};

class Flutist : public Musician
{
 public:
 Flutist(void) { ; }
 virtual void Play(void) { printf("The flute sounds.\n"); }
};

void VeryBad(Flutist **f, Pianist **p)
{
 Musician **m1=f;
 Musician **m2=p;
 *m1=*m2; // Oh no! **f is supposed to be a Flutist and it's a Pianist!
}

int main(void)
{
 Flutist *f=new Flutist();
 Pianist *p=new Pianist();
 VeryBad(&f, &p);
 printf("Mom is asleep, but flute playing wont bother her.\n");
 f->Play(); // Since f is a Flutist* this can't possibly play piano, can it?
}

And here it is in action:

$ g++ -fpermissive verybad.cpp -o verybad
verybad.cpp: In function void VeryBad(Flutist**, Pianist**):
verybad.cpp:26:20: warning: invalid conversion from Flutist** to Musician** [-fpermissive]
verybad.cpp:27:20: warning: invalid conversion from Pianist** to Musician** [-fpermissive]
$ ./verybad 
Mom is asleep, but flute playing wont bother her.
The piano blares
最美的太阳 2024-12-20 19:33:55

不允许 Vehicle**vehiclePtrPtr = carPtrPtr;,因为它是 Derived**Base** 的转换,这是不允许的。

您的示例中说明了不允许的原因。

   Car   car;
   Car*  carPtr = &car;
   Car** carPtrPtr = &carPtr; 

这样 carPtrPtr 就指向 Car 的指针。

核潜艇;
NuclearSubmarine* subPtr = ⊂

这也是合法的,但如果您可以这样做,

Vehicle** vehiclePtrPtr = carPtrPtr;

您可能会意外地

*vehiclePtrPtr = subPtr;

这样做,因为 *vehiclePtrPtr 是指向 Car 的指针。在最后一行中,您为其分配一个指向 Sub 的指针。因此,您现在可以在 Car 类型的对象上调用派生类 Sub 中定义的方法,但具有未定义的行为。

Vehicle** vehiclePtrPtr = carPtrPtr; is not allowed because it is a Derived** to Base** conversion, which is not allowed.

Reason why it is not allowed is illustrated in your example.

   Car   car;
   Car*  carPtr = &car;
   Car** carPtrPtr = &carPtr; 

so that carPtrPtr points to a pointer to Car.

NuclearSubmarine sub;
NuclearSubmarine* subPtr = ⊂

this is legal also, but if you could do

Vehicle** vehiclePtrPtr = carPtrPtr;

you could accidentaly do

*vehiclePtrPtr = subPtr;

that is *vehiclePtrPtr is a pointer to a Car. with this last line, you assign to it a pointer to a Sub. Thus, you could now call a method defined in derived class Sub on a object of type Car, with undefined behavior.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文