从派生**转换为基础**

时间:2011-11-06 08:59:40

标签: c++ inheritance pointers c++-faq pointer-conversion

我正在阅读this,遗憾的是,我无法理解为什么编译器不允许从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()!

3 个答案:

答案 0 :(得分:20)

这与一碗香蕉不是一碗水果的原因基本相同。如果一碗香蕉 一碗水果,你可以把一个苹果放入碗中,它将不再是一碗香蕉。

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

与你的例子相同。如果有从Derived**Base**的转换,您可以将指针指向一个苹果类型系统承诺只有指向香蕉的指针可能存在。吊杆!

答案 1 :(得分:11)

这不允许出现无意义的错误:

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

答案 2 :(得分:0)

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的指针。因此,您现在可以在类型为Sub的对象上调用派生类Car中定义的方法,并且具有未定义的行为。