derive to base

can anyone explain that?????

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base
{
public:
	virtual void foo();

};
class Derived: public Base
{

};
void f(Base * &a)
{

}
void main( void )
{

Derived *d=new Derived;
f(d);

getchar();
}


it says
cannot convert parameter 1 from 'Derived *' to 'Base *&'
That's because your function f is taking a Base* by reference and since it's not const you can potentially modify what it points to inside your function. Remove the & reference and it should work.

As to why the compiler doesn't allow it, consider this code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Derived2 :  public Base {};

void f(Base* &a)
{
  // hey we're assigning Derive2 to a Base *
  // but that's ok because Derive2 is-a Base after all right?? ;)
  a =  new Derived2(); 
}

int main()
{
  Derived *d = new Derived;
  f(d);
  // What the heck is d pointing to now??
  // A Derived type  != Derived2
}
but this work

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Base
{
public:
	virtual void foo();

};
class Derived: public Base
{

};
void f(Base * &a)
{

}
void main( void )
{

Base *d=new Derived;
f(d);

getchar();
}


and in this case it can change d inside f..
what do you think???
and then in main Base* will be pointing to a Derived2 which is totally valid. The problem in the first one is that that in the end Derived* points to a Derived2 even though that's invalid.
sory i dont understand this
why is that invalid for derived* and not for base *??
Last edited on
closed account (z05DSL3A)
cannot convert parameter 1 from 'Derived *' to 'Base *&'

The function is called with a parameter that is not of the type that the function expects. This leads to the creation of a temporary object. The temporary object is used to initialize the reference. If the reference is not const the then the error is raised.

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
#include <iostream>
using namespace std;


class Base
{
public:
    virtual void foo()
    {
        cout << "Base::foo()" << endl;
    }
};
class Derived: public Base
{
    virtual void bar()
    {
        cout << "Base::bar()" << endl;
    }
};
void f(Base * const &a)
{
    a->foo();
}

int main()
{

    Derived *d=new Derived;
    
    f(d);

}
Last edited on
vagelis wrote:

sory i dont understand this
why is that invalid for derived* and not for base *??


you need to take a step back and answer these questions:

- Can a derived be used as a base?
- Can a derived2 be used as a base?
- Can a base be used as a derived?
- Can a derived be used as a derived2?
Last edited on
Hold on.

Original code (slightly edited for brevity and to demonstrate the problem):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Base {
	virtual void foo() { x = 99; }
        void bar() { x = 111; }
        int x;
};

struct Derived: Base {
       virtual void bar() { y = 42; }
       int y;
};

void f( Base*& a ) {
}

int main() {
    Derived* d = new Derived;
    f( d );
}


Ok. There is a very subtle problem here, and it has to do with how the compiler deals with inheritance and how it
compiles member functions.

When D derives from B (to abbreviate Derived and Base), the memory for an instance of D is laid out such that
all of the memory used by the direct (non-inherited) members of D are together and all the members of B are
together. So, in my above example, The layout of an instance of B is as follows:


"this" ptr offset          what is stored there
0x0000                     vtable pointer for B
0x0004                     int x


When the compiler compiles Base::foo, it knows the "this" pointer (passed implicitly as parameter), and it knows
that x is 4 bytes higher than the "this" pointer, so it writes 99 to *(this + 4). Likewise, when it compiles Base::bar,
it knows the "this" pointer (passed implicitly as parameter), and it knows that x is bytes higher than the "this"
pointer, so it writes 111 to *(this + 4).

The layout of an instance of D is as follows (this isn't quite correct, but it demonstrates the point below):


"this" ptr offset          what is stored there
0x0000                     vtable pointer for D
0x0004                     int y
0x0008                     int x


Now, here's the problem. Suppose you have a pointer-to-D, such as in the example above. If you call
Base::bar(), it wants to write 111 to *(this + 4). But, look at the layout. Offset 0x0004 is no longer
x, the variable that Base::bar wants to modify, but instead it is y!

How can this work? It works because the compiler is silently generating some "fixup" code for you.

1
2
3
4
int main() {
    Derived* d = new Derived;
    d->bar();
}


On the call to Base::bar, the compiler says: ok, you are calling bar() with a Derived*, but bar() is a member
of Base. Instead of passing in the exact pointer value of d, I'm going to kludge it a bit -- I'm going to ADD 4
to the pointer, and pass that. So I'm going to pass, essentially ( d + 4 ) as the "this" pointer to Base::bar.
Now when Base::bar takes the "this" pointer and does *( this + 4 ) = 111, it actually points to x! Voila.

Incidentally, the fact that the compiler has to generate this "fixup" code means that typecasts are not
necessarily free; indeed, they can and do generate code. But that is beside the point here.

Now, go one step further. In the declaration of f(), we take a pointer-to-Base by non-const reference,
meaning that the user intends to modify the pointer value and have the caller see the change. The
problem is that the compiler had to generate the fixup code in the first place, so in fact, you are
attempting to bind a non-const reference to a temporary variable (that variable being the kludged
"this" pointer), and the C++ standard does not allow you to do that.

And that is why you get the compile error.

The reason why it works for this main:

1
2
3
4
int main() {
    Base* b = new Derived;  // Line A
    f( b );
}


is because the "fixup" code is generated on Line A, and the result of the fixed up pointer is actually stored in
memory, in variable b. Thus f(b) can be called, because no fixup code needs to be generated there.
Last edited on
Topic archived. No new replies allowed.