Преобразование из производного ** в базу **
Я читал это и, к сожалению, не мог глубоко понять, почему компилятор не позволяет преобразование из 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()!
3 ответа
По сути, это та же самая причина, почему ваза с бананами - это не ваза с фруктами. Если бы миска с бананами была миской с фруктами, вы могли бы положить яблоко в миску, и это больше не было бы миской с бананами.
Пока вы только осматриваете чашу, конверсия безвредна. Но как только вы начинаете изменять его, преобразование становится небезопасным. Это ключевой момент, который нужно иметь в виду. (Это точная причина, по которой неизменные коллекции Scala действительно позволяют преобразование, но изменяемые коллекции запрещают его.)
То же самое с вашим примером. Если было преобразование из Derived**
в Base**
Вы можете поместить указатель на яблоко, если система типов обещает, что может существовать только указатель на банан. Boom!
Нет недостатка в бессмысленных ошибках, которые позволили бы:
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
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
с неопределенным поведением.