pointer polymorphism?

Jan 16, 2015 at 7:10pm
What rule am I breaking to get "invalid conversion from 'Der**' to 'Base**' [-fpermissive]"?
A pointer to a derived class is type-compatible with a pointer to its base class.
So I don't think I am breaking a polymorphism rule.

Here is the example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>

class Base
{
	public:
		void print() { std::cout << " Base"; }
};

class Der: public Base
{
	public:
		void print() { std::cout << " Der"; }
};

class Container
{
	private:
		Base** ptrsBase;		//array of Base pointers
	public:
		//Base* parameter does not accept Der* even though it is a kind of Base*
		Container(Base* pb[]): ptrsBase(pb) {}
};

int main()
{
	Base b;
	Der d;

	//Base* ptrsArray[] = { &b, &d };	//polymorph works when array type is Base*
	Der* ptrsArray[] = { &d, &d };		//compile fails when array type is Der*
	Container containter(ptrsArray); //error: invalid conversion from 'Der**' to 'Base**' [-fpermissive]

	b.print();
	d.print();
}

compile error messages:
C:\Users\wolf\Documents\>g++ polymorph_pointer.cpp
polymorph_pointer.cpp: In function 'int main()':
polymorph_pointer.cpp:31:32: error: invalid conversion from 'Der**' to 'Base**'[-fpermissive]
  Container containter(ptrsArray);
                                ^
polymorph_pointer.cpp:21:3: error:   initializing argument 1 of 'Container::Container(Base**)' [-fpermissive]
   Container(Base* pb[]): ptrsBase(pb) {}
   ^

Thank you.
Jan 16, 2015 at 10:46pm
//Base* parameter does not accept Der* even though it is a kind of Base*
Wrong.
Der is a kind of Base, therefore pointers to Der are convertible to Base. Der* is not a kind of Base* so it is impossible to freely convert those.

It is not only syntax pedancy, it is also saves you from errors like:
1
2
3
4
5
6
7
8
9
struct B      {};
struct D1 : B {};
struct D2 : B {};

D1** foo; 
//Fill foo
B** bar = (B**)foo; //Bad
bar[0] = new D2; //Now first element of foo array points to D2
foo[0].do_something();//Warning, called on actually D2 object. D1 and D2 are not compatible at all!! 
Jan 17, 2015 at 2:13am
MiiNiPaa,
Thank you for the counter example. It made me understand.
Here is my interpretation of your example:
1
2
3
4
5
6
7
8
9
10
11
struct B      {};	//B is a base class
struct D1 : B {};	//D1 is a kind of B
struct D2 : B {};	//D2 is a kind of B

D1** foo;		//foo is an array of D1 pointers
B** bar;		//bar is an array of B pointers
bar = foo;		//bar array is foo array (this should no compile)
D2 d2;			//d2 is a D2 object
bar[0] = &d2;		//first element of foo array points to a D2 object
			//foo was supposed to be an array of just D1 pointers
			//this is why the compiler insists that pointer types match 
Jan 17, 2015 at 2:23am
All Der instances are also Base instances, so it's safe to case from a Der* to a Base*. But not all Base instances are Der instances, so you can't cast the other way. To see this, add some data to the classes and force bArray to work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

class Base {
public:
    Base(int i=0) : ival(i) {}
    int ival;
};

class Der : public Base {
public:
    Der(double d) : Base(0), dval(d) {}
    int dval;
};


int
main()
{
    Base b(1), *bp=&b;
    Der d(2.0), *dp = &d;

    Base *bArray[] = {&b, &d};
    Der *dArray[] = {reinterpret_cast<Der*>(&b), &d};

    for (int i=0; i<2; ++i) {
        std::cout << "bArray[" << i << "].ival = " << bArray[i]->ival << '\n';
        std::cout << "dArray[" << i << "].dval = " << dArray[i]->dval << '\n';
    }
    return 0;
}
$ ./foo
bArray[0].ival = 1
dArray[0].dval = 2272360
bArray[1].ival = 0
dArray[1].dval = 2

Here dArray[0].dval is garbage because dArray[0] points to b, which is a Base and doesn't even have a dval member.

The bottom line is that you can cast a pointer or reference to a derived class into a pointer/reference to the Base class because there is always a Base instance there. But the reverse isn't always true.
Jan 17, 2015 at 5:27am
Thanks dhayden,
Your example brings clarity to inheritance and pointer-types.
Topic archived. No new replies allowed.