Преобразование из производного ** в базу **
Я читал это и, к сожалению, не мог понять, почему компилятор не разрешает преобразование из Derived ** в Base **. Также я видел это, что дает больше информации, чем ссылка parashift.com.
РЕДАКТИРОВАТЬ:
Проанализируем этот код по строкам:
- Указатель функций, отлитый от другой подписи
- Скрытие нулевых значений, понимание того, почему голанг не удается здесь
- Должен ли кто-нибудь установить указатели на «NULL» после освобождения?
- Что такое указатель void и что такое пустой указатель?
- Является ли NULL всегда ложным?
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()!
- Копирование структуры, содержащей указатели на устройство CUDA
- Есть ли у Java указатели?
- Предоставляет ли компилятор возможность переработать освобожденные переменные указателя?
- Арифметика указателей: ++ * ptr или * ptr ++?
- Ошибка сегментации при изменении строки с помощью указателей?
- указатель на массив c ++
- Как сделать буквенный * int64 в Go?
- Что я могу использовать вместо оператора стрелки, `->`?
Это в основном по той же причине, почему миска с бананами – это не чаша фруктов. Если чаша из бананов была чашей с фруктами, вы могли бы положить яблоко в миску, и она больше не была бы баной для бананов.
Пока вы только проверяете чашу, конверсия безвредна. Но как только вы начнете его изменять , преобразование становится небезопасным. Это ключевой момент, чтобы иметь в виду. (Это точная причина, по которой неизменяемые коллекции 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 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; NuclearSubmarine * subPtr = ⊂
это также законно, но если бы вы могли это сделать
Vehicle** vehiclePtrPtr = carPtrPtr;
вы можете случайно
*vehiclePtrPtr = subPtr;
это *vehiclePtrPtr
– указатель на Car
. с этой последней строкой вы назначаете ему указатель на Sub
. Таким образом, теперь вы можете вызывать метод, определенный в производном classе Sub
для объекта типа Car
с неопределенным поведением.