提问人:Narek 提问时间:11/6/2011 最后编辑:CommunityNarek 更新时间:5/9/2017 访问量:2335
从派生**到基数**的转换
Conversion from Derived** to Base**
问:
我正在阅读这篇文章,不幸的是,我无法深入理解为什么编译器不允许从 Derived** 转换为 Base**。我也看到了这一点,它提供的信息只比 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()!
答:
11赞
David Schwartz
11/6/2011
#1
不乏无意义的错误,这将允许:
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
评论
1赞
Alok Save
11/6/2011
不更正您的类在此处派生自不同的基类:P
0赞
Troubadour
11/6/2011
为什么分配给不好?在那个阶段,您所做的只是将其分配给 .*m1
*m2
Musician*
0赞
David Schwartz
11/6/2011
因为现在是一名钢琴家。**f
0赞
463035818_is_not_an_ai
5/9/2017
我不明白这个例子。“// 既然 p 是长笛演奏家*,这不可能弹钢琴,对吧?”,但是一个,它应该弹钢琴,它在弹钢琴,问题出在哪里?在分配了一个 ,但如果这导致了一个问题,它要么没有在示例中演示,要么我完全错过了这一点p
Pianist*
VeryBad
Flutist*
Pianist*
1赞
463035818_is_not_an_ai
5/9/2017
没关系。我不确定这个例子或我的理解是否有问题。我在其他地方读过一点,现在或多或少很清楚了。你的例子真的有很大帮助。
23赞
fredoverflow
11/6/2011
#2
这基本上与一碗香蕉不是一碗水果的原因相同。如果一碗香蕉是一碗水果,你可以把一个苹果放进碗里,它就不再是一碗香蕉了。
只要你只检查碗,转换是无害的。但是一旦你开始修改它,转换就会变得不安全。这是要牢记的关键点。(这就是为什么不可变的 Scala 集合实际上允许转换,但可变集合禁止转换的确切原因。
与您的示例相同。如果有从 到 的转换,你可以放置一个指向苹果的指针,如果类型系统承诺只能存在一个指向香蕉的指针。繁荣!Derived**
Base**
评论
1赞
David Schwartz
11/6/2011
很好的类比。我必须偷^H^H^H^H^H^H使用它。“如果你能把一碗可修改的香蕉变成一碗可修改的水果,你可以把一个苹果放进去,它就不再是一碗香蕉了。
1赞
fredoverflow
11/6/2011
@David:实际上,我自己从强大的乔恩·斯基特那里偷来的。
0赞
Narek
11/9/2011
关于这一点:“如果你能把一碗可修改的香蕉变成一碗可修改的水果,你可以把一个苹果放进去,它就不再是一碗香蕉了。 在记忆中如何表示它是一碗香蕉,如果我用它来做苹果,可能会出现什么样的问题?
0赞
fredoverflow
11/9/2011
@Narek:你的问题没有实际意义,因为没有从 到 的转换。你不能把一碗香蕉当成一碗水果,因此你甚至不能尝试在碗里涂上药。类型系统将触发编译时错误。Derived**
Base**
2赞
Calmarius
10/6/2018
那为什么我不能投一个呢?恒定性将阻止我将派生指针指向其他派生指针。Derived**
Base * const *
0赞
kiriloff
5/5/2013
#3
Vehicle** vehiclePtrPtr = carPtrPtr;
不允许,因为它是不允许的 to 转换。Derived**
Base**
示例中说明了不允许这样做的原因。
Car car;
Car* carPtr = &car;
Car** carPtrPtr = &carPtr;
因此,它指向指向 的指针。carPtrPtr
Car
核潜艇; 核潜艇* subPtr = ⊂
这也是合法的,但如果你能做到的话
Vehicle** vehiclePtrPtr = carPtrPtr;
你可能会不小心这样做
*vehiclePtrPtr = subPtr;
即是指向 .使用最后一行,您可以为其分配一个指向 .因此,您现在可以在具有未定义行为的对象上调用派生类中定义的方法。*vehiclePtrPtr
Car
Sub
Sub
Car
评论
NuclearSubmarine*
Car
NuclearSubmarine
Car