Lets assume the following code compiles:
int main()
{
A* p = new B(); // 1
C& c = p->method(); // 2
*c = *p; // 3
c = *p; // 4
}
given there is not a single operator= that expects the class A,B, C and not a single casting operator to any of those to any of those.
Is it possible that B inherits from A inherits from C?
And how is it possible that both line 3 and line 4 will compile?
This line
*c = *p
could never compile without overloading the *operator for C. References, while having a lot of similarities to pointers, are not pointers. They are not memory addresses and thus cannot be dereferenced. Instead, they act as pointers that are automatically dereferenced whenever used. Furthermore, references can't be reassigned to point to different objects so although the code would compile if you changed it to c = *p, it wouldn't work as intended. c would still be pointing to the object that method returned.
The relations you described between A, B and C could be the case if you made c a pointer to C instead of a reference, as well as remove the line 4. The code would then look like this:
int main()
{
A* p = new B(); // works because B inherits from A
C* c = p->method(); // works because is "method" is a member function
// of A and returns a pointer to an instance of C
c* = *p; // works because A inherits from C
}
Related
The C++ standard says "... the interpretation of the call of a virtual function depends on the type of the object for which it is called (the dynamic type)" (p. 252) and then
"if a pointer p whose static type is 'pointer to B' is pointing to an object of class D, derived from B, the dynamic type of *p is D" (p. 2). Here B is a base class and D a derived class.
This seems to suggest (to me) that if I say
D d;
B *p = new B();
*p = d;
then, if f() is virtual in B, p->f() should call D::f(), which is wrong. I guess I'm not clear about the meaning of "pointing to an object of class ...". I know, if I say p = &d, then D::f() is called, but I would like to know why the above is wrong.
D d;
B *p = new B();
*p = d;
That final line assigns d to *p. This means it will copy the D instance using the B assignment operator and the object will be sliced. The dynamic type of *p in that case is still B.
p = &d;
That assigns the pointer to the D object to p. No slicing will occur in this case, because you are just assigning the pointers, not the objects themselves. The dynamic type of *p in this case is D.
The crux of the answer is that in C++, the dynamic type of an object can never change. You're thinking about the assignment expression *p = d as somehow completely replacing the object at *p with the object d. But that's not what happens. In C++, an object can never truly replace another.
Since class types are involved, *p = d just calls the assignment operator of class B (that's the static type of *p) with an argument D. An object in C++ can only be acted upon, it cannot really be "replaced."
Sure, we talk about copying objects, assigning to them etc. But that's just for convenience, as most of the time, the very exact semantics doesn't matter and thinking about = as assigning one object to another is simple. But deep down, it's either a function call on the target object (for class types), or copying the value of some bits into the target object's space (for primitive types). The target object itself always remains.
As TartanLlama said above, in the third line you're "wrongfully" assigning d to the object pointed by P, thereby what we refer to as Slicing occurs. From Professional C++,
...When upcasting, use a pointer or reference to the superclass to avoid slicing
D d;
B *p = new B();
*p = d;
This means the correct code evaluates to the code hereunder
D d;
B *p = new B();
p = &d;
OR
D d;
B *p = new B();
p = static_cast<B*>(&d);
Consequently, the dynamic type of the object pointed by p (*p) is still D. This allows you to switch back and forth through the inheritance hierarchy by upcasting or downcasting the pointer to the child class and not the object.
class B;
class A{
B *b;
public:
void operator= (B *b){
this->b = b;
}
};
B *b = new B()
A *a = new A();
a = b;
I get a "cannot convert B* to A*" error.
Is there a way around this?
Now, if there is a way, and if I use something like:
a = NULL;
Which operator "=" would be used?
You have assigned the pointer instead of the object. Simply replace the last instruction with:
*a = b;
To answer the second question: NULL can be defined in more than one way in the compiler (as of the latest standard, either as the integral 0 or the literal nullptr). Pointers can also be cast to pointer of other types, but passing a void* to an overloaded function that takes an int* or a long* may make the compiler unable to resolve the function being called.
If however, you want to avoid NULL, simply make operator(B& b) instead. References are sure to be pointing at an object.
Your operator= provides assignment from a B* to an A. Your code does not provide a conversion from a B* to a A* (as the error message shows). As such, a=NULL will not use the assignment operator you provided, since a is a pointer, not an A. Your code allows assignment from a B* to an A, like A a= new B();.
If you meant to be using actual objects instead of pointers, remove all the * from your code:
class B{};
class A{
B b;
public:
void operator= (const B& b){ //pass non-primitives by const reference
this->b = b;
}
};
B b;
A a;
a = b;
If you wanted to be using pointers, the only "useful" way to assign a B* to an A* is if a B object derives from A. That appears to not be what you're doing, so assigning a B* to an A* would make no sense in your code.
Change your statement that assigns to a to:
*a = b;
I am looking into the pointers and I came up with code like that
class b
{
}
class d
{
}
d* a = 0;
b *t = new b();
*t = * ( b* )a;
What does this declaration mean?
What value does t have?
t will still be pointing to the object created with new b();. *t = changes the value of what t points to, not t itself.
Your last line contains undefined behavior as you're derefenrencing a null pointer. Also, your cast is actually a reinterpret_cast in disguise, which is something you shouldn't do.
I have a question about C++, how to assign a Base object to a Derived object? or how to assign a pointer to a Base object to a pointer to a Derived object?
In the code below, the two lines are wrong. How to correct that?
#include <iostream>
using namespace std;
class A{
public:
int a;
};
class B:public A{
public:
int b;
};
int main(){
A a;
B b;
b = a; //what happend?
cout << b.b << endl;
B* b2;
b2 = &a; // what happened?
cout << b->b << endl;
}
It makes no sense to assign a base object to a derived (or a base pointer to a derived pointer), so C++ will do its best to stop you doing it. The exception is when the base pointer really points at a derived, in which case you can use dynamic cast:
base * p = new derived;
derived * d = dynamic_cast <derived *>( p );
In this case, if p actually pointed at a base, the pointer d would contain NULL.
When an object is on the stack, you can only really assign objects of the same type to one another. They can be converted through overloaded cast operators or overloaded assignment operators, but you're specifying a conversion at that point. The compiler can't do such conversions itself.
A a;
B b;
b = a;
In this case, you're trying to assigning an A to a B, but A isn't a B, so it doesn't work.
A a;
B b;
a = b;
This does work, after a fashion, but it probably won't be what you expect. You just sliced your B. B is an A, so the assignment can take place, but because it's on the stack, it's just going to assign the parts of b which are part of A to a. So, what you get is an A. It's not a B in spite of the fact that you assigned from a B.
If you really want to be assigning objects of one type to another, they need to be pointers.
A* pa = NULL;
B* pb = new B;
pa = pb;
This works. pa now points to pb, so it's still a B. If you have virtual functions on A and B overrides them, then when you call them on pa, they'll call the B version (non-virtual ones will still call the A version).
A* pa = new A;
B* pb = pa;
This doesn't work. pa doesn't point B, so you can't assign it to pb which must point to a B. Just because a B is an A doesn't mean than an A is a B.
A a;
B* pb = &a;
This doesn't work for the same reason as the previous one. It just so happens that the A is on the stack this time instead of the heap.
A* pa;
B b;
pa = &b;
This does work. b is a B which is an A, so A can point to it. Virtual functions will call the B versions and non-virtual ones will call the A versions.
So, basically, A* can point to B's because B is an A. B* can't point to A because it isn't a B.
The compiler won't allow that kind of thing. And even if you manage to do it through some casting hack, doing so makes no sense. Assigning a derived object to a pointer of a base makes sense because everything that base can do, derived can do. However, if the opposite case was allowed, what if you try to access a member defined in derived on a base object? You would be trying to access an area of memory filled with garbage or irrelevant data.
b = a; //what happend?
This is plain illegal - A is not B, so you can't do it.
b2 = &a; // what happened?
Same here.
In neither case, the compiler wouldn't know what to assign to int b, hence he prevents you from doing that. The other way around (assigning Derived to Base) works, because Base is a subset of Derived.
Now if you would tell us, what exactly you want to achieve, we might help you.
If it's a case of assigning an A that is known to be a Derived type, you can do a cast:
A* a = new B();
B* b = dynamic_cast<B>(a);
Just remember that if a is not a B then dynamic_cast will return NULL. Note that this method works only on pointers for a reason.
Derived object is a kind of Base object, not the other way around.
I have seen this in our code a couple times and it immediately makes me suspicious. But since I don't know the original intent I am hesitant to remove it.
//requires double indirection which I won't go into
FooClass::FooFunction(void ** param)
{
//do something
}
SomeClass * A = new SomeClass();
SomeClass **B = &A;
FooFunction( reinterpret_cast<void**>(&*B) ); // what is happening here?
The "&*B" part is the part in question? Feel free to integrate explanation of the reinterpret cast but I am pretty familiar with cast techniques.
I've done similar things with iterators - dereference the iterator to get a reference, and then do the "&" operator to get a pointer.
I don't see why it would be doing anything here though. If the type to the right of "&*" is a pointer type it does nothing.
I can see only one reason for this: B has overloaded operator*() to return an X, but whoever wrote the code needed an X*. (Note that in your code, X is A*.) The typical case for this is smart pointers and iterators.
If the above isn't the case, maybe the code was written to be generic enough to deal with smart pointers/iterators. Or it used to use smart pointers and whoever changed it didn't bother changing &*, too? Have you poked through its history to see when this was introduced and what the code looked then?
Consider the following sample;
class A {
public:
int f() { return 55; }
};
class B {
public:
B( A* a ) : a(a) {}
A*& operator*() { return a; }
A* a;
};
int main () {
A* a = new A();
B b = a;
// &* calls operator*, then gets address of A
void** x = reinterpret_cast<void**>(&*b);
cout << reinterpret_cast<A*>( *x )->f() << endl; // prints 55
void** x2 = reinterpret_cast<void**>( b ); // compile error
}
Your last edit of question leads to:
A* a = new A();
A** b = &a;
void** x = reinterpret_cast<void**>(&*b); // now this is equal to the following
void** x2 = reinterpret_cast<void**>( b ); // so using &* make no sense