I have derived class D from base class B, like follow:
class D : public B
{
//staff or nothing
}
I want to process received pointer B* b
, in a way that declare D* d
, and initialize *d
with all same property values from *b
. What is possible or best way to do it, if i have no rights to change B
implementation? Something like
// I have pointer to B object, B* b received as a result of some function
class D : public B
{
//staff or nothing
}
D *d; //declaration
// I want to initialize d, something like d=b (if that would be legal)
// or d = static_cast <D*>(b);
Thanks
Notation:
B b;
B* pb = &b;
D* pd;
If you have the pointer pb
and want to have a pointer pd
, where *pd
has the same (values of) data members as *pb
, then you have to copy or move the data from *pb
to some (memory) location and let pd
point to that location.
C++ does not allow a conversion from &b
to pd
neither implicit ( pd = &b
) nor explicit ( pd = static_cast<D*>(&b)
) without resulting in undefined behaviour. Undefined behaviour is as bad as it can get, as you don't have any guarantees what will happen next (crash, data corruptions, heisenbugs, ..).
static_cast
ptr conversions Let's introduce some naming from the Standard to make things more clear:
B* pb = &d;
Then the static type of *pb
is B
whereas the dynamic type of *pb
is D
. Static type is basically what the compiler sees, and dynamic type is what is actually in there at run time.
The following conversion is fine:
D d;
pb = &d; // `pb` points to `B`-type subobject of `d`
pd = static_cast<D*>(pb); // reverts the cast above to get the original `&d`
The last line is fine in case *pb
has the dynamic type D
(or a derived type of D
). Else, undefined behaviour. That is why there's dynamic_cast
:
pd = dynamic_cast<D*>(pb);
Again, if *pb
is of dynamic type D
, everything is fine (same as above). But if *pb
is not of dynamic type D
, dynamic_cast
returns a null pointer value ( nullptr
). This way, no undefined behaviour, you can check whether pd
is nullptr
now or not. As dynamic_cast
is substantially slower than static_cast
, if you really know that the conversion will succeed, you can use static_cast
instead ( see the Qt example ).
Now what if you want to have a pointer pd
such that all data members ("properties") are the same as the data members of &b
? You have to create an object of type D
and copy or move the data from &b
to this new object. This is what Subaru Tashiro's proposed in Method 2, like:
class D : public B
{
public:
// copy:
D(B const& b) : B(b) {} // if you cannot use B-copy-ctor than have to provide own definition
// move:
D(B&& b) : B(b) {} // same issue as above
};
B b;
B* pb = &b;
D d1(*pb); // this is how you can copy
D d2( std::move(*pb) ); // this is how you can move
D* pd = &d1; // or = &d2;
Note both the copy and move line create a new D
-type object to store the copied/moved data in. You don't need to provide both copy and move support.
There are other possibilities like wrapping the B
-type object, but they differ from your approach of a derived class.
pd = &b
is bad D
child class are problematic. If the D
class adds any data members, how should C++ / the compiler know how they should be initialized? It cannot just leave them uninitialized as this would be error prone (consider class invariants). Memory problems. Example: Imagine a D
-type object would look like this in memory:
|DD[BBBB]DDDDDDDD| ^start of B sub-object ^start of D object
Now, when you do pd = static_cast<D*>(pb)
, the address contained in pb
would be interpreted as start of B sub-object and be decreased by 2 bytes to get the start of D object .
b
object, it's only guaranteed you can access the memory from (char*)pb
to (char*)pb + sizeof(b)
(w/o considering alignment inside b
). It could be possible that accessing the memory before (char*)pb
or after (char*)pb + sizeof(b)
leads to an error, eg the CPU complaining about you accessing virtual memory that has not been mapped to physical memory. It's more probable that there's other meaningful data before (char*)pb
and after (char*)pb + sizeof(b)
. By writing to any data member of static_cast<D*>(pb)
that has been introduced in the D
class, you can corrupt this other data (not sure about the other data members). *pd
, eg if your compiler assumes all D-type objects start at a 2-byte boundary and all B-type objects start at a 4-byte boundary (tricky and pathologic, but a possible problem). D
, it won't be initialized by the pointer conversion. Calling any virtual method of static_cast<D*>(pb)
that has been introduced in the D
class is therefore not a good idea. dynamic_casts
, so it's not only typeid
that's affected. B
sub-object of *pd
with some compilers / C++ implementations w/o problems, there's no guarantee it will work with all compilers / versions. The points above are some of the problems that came to my mind, but I'm by no means an expert. Essentially, there's no way to predict / no guarantee what will happen when you perform this conversion and use the B
sub-object of the resulting pd
. Final remarks:
static_cast
reference for this: 5.2.9/2, last 2 sentences reinterpret_cast
and a lot of comments around this to indicate you know what you're doing right there and that it is a hack. Method 1: You can use static_cast
to use a base class to initialize a derived class but this will result in an incomplete derived object.
static_cast can perform conversions between pointers to related classes, not only from the derived class to its base, but also from a base class to its derived. This ensures that at least the classes are compatible if the proper object is converted, but no safety check is performed during runtime to check if the object being converted is in fact a full object of the destination type. Therefore, it is up to the programmer to ensure that the conversion is safe.
See this example:
here we write the base class that has a public variable called "bInt"
class B {
public:
int bInt;
};
here we create a subclass that also has it's own variable called "dInt"
class D: public B {
public:
int dInt;
};
here we have a new instance of the base class. We initialize the base object's variable.
B * baseObj = new B;
baseObj->bInt = 1;
Here we use the base class to declare and initialize the derived class by using static_cast
. Note that at this point, *derivedObj
is incomplete. To be specific, derivedObj->dInt
has an undefined value.
D * derivedObj = static_cast<D*>(baseObj);
Because D is derived from B, it also has the bInt
variable. And because we used static_cast
to initialize *derivedObj
, the value of it's bInt
is also the same as *baseObj
's bInt
, therefore they are equal.
if(baseObj->bInt == derivedObj->bInt)
{
display("it's equal");
}
But because the base class has no dInt
variable, this variable will be left uninitialized and the outcome of this procedure is undefined.
int myNum = derivedObj->dInt;
If you plan to use static_cast, be sure to initialize members of the derived class. It's a good habit to do so anyway.
When using static_cast, you don't need to know all of the members of B. D automatically has all of B's member values. But what you're trying to do is dangerous because you are creating an incomplete object.
Method 2: If you really need to subclass B, then write D's constructor as one that takes a B* and initializes it's B by copying like so:
D(const B &pass) : B(pass)
So when you want to declare and initialize an object of type D this is how you do it.
B *baseObj = new B;
D *derivedObj = new D(*baseObj);
So in summary, you have two choices:
Both of these methods have the same outcome, the difference is how the program does this behind the scenes.
Unfortunately in your example, using d=b
is 'illegal' (?) because you are trying to assign a base class to a derived class. You can only use the assignment operator the other way around such as b=d
given that d
is initialized.
You can also write your own assignment operator to do that but I personally don't recommend it.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.