Conversion from Derived** to Base**

后端 未结 3 710
终归单人心
终归单人心 2020-12-01 20:59

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

相关标签:
3条回答
  • 2020-12-01 21:36

    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.

    0 讨论(0)
  • 2020-12-01 21:39

    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!

    0 讨论(0)
  • 2020-12-01 21:40

    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
    
    0 讨论(0)
提交回复
热议问题