22.2.1 dynamic_cast1
The dynamic_cast operator takes two operands: a type bracketed by < and > , and a pointer or reference bracketed by ( and ) . Consider first the pointer case: dynamic_cast<T∗>(p)
If p is of type T∗ or of a type D∗ where T is a base class of D , the result is exactly as if we had simply assigned p to a T∗ . For example:
class BB_ival_slider : public Ival_slider, protected BBslider {
// ...
};
void f(BB_ival_slider∗ p)
{
Ival_slider∗ pi1 = p; // OK
Ival_slider∗ pi2 = dynamic_cast<Ival_slider∗>(p); // OK
BBslider∗ pbb1 = p; // error : BBslider is a protected base
BBslider∗ pbb2 = dynamic_cast<BBslider∗>(p); // OK: pbb2 becomes nullptr
}
This (the upcast) is the uninteresting case. However, it is reassuring to know that dynamic_cast doesn’t allow accidental violation of the protection of private and protected base classes. Since a dynamic_cast used as an upcast is exactly like a simple assignment, it implies no overhead and is
sensitive to its lexical context.
The purpose of dynamic_cast is to deal with the case in which the correctness of the conversion
cannot be determined by the compiler. In that case, dynamic_cast<T∗>(p) looks at the object pointed to by p (if any). If that object is of class T or has a unique base class of type T , then dynamic_cast returns a pointer of type T∗ to that object; otherwise, nullptr is returned. If the value of p is nullptr ,
dynamic_cast<T∗>(p) returns nullptr . Note the requirement that the conversion must be to a uniquely identified object. It is possible to construct examples where the conversion fails and nullptr is returned because the object pointed to by p has more than one subobject representing bases of type
T (§22.2).
A dynamic_cast requires a pointer or a reference to a polymorphic type in order to do a downcast or a crosscast. For example:
class My_slider: public Ival_slider { // polymor phic base (Ival_slider has virtual functions)
// ...
};
class My_date : public Date { // base not polymorphic (Date has no virtual functions)
// ...
};
void g(Ival_box∗ pb, Date∗ pd)
{
My_slider∗ pd1 = dynamic_cast<My_slider∗>(pb); // OK
My_date∗ pd2 = dynamic_cast<My_date∗>(pd); // error : Date not polymorphic
}
Requiring the pointer’s type to be polymorphic simplifies the implementation of dynamic_cast because it makes it easy to find a place to hold the necessary information about the object’s type. A typical implementation will attach a "type information object"(§22.5) to an object by placing a pointer to the type information in the virtual function table for the object’s class (§3.2.3). For example:
The dashed arrow represents an offset that allows the start of the complete object to be found given only a pointer to a polymorphic subobject. It is clear that dynamic_cast can be efficiently implemented. All that is involved are a few comparisons of type_info objects representing base classes; no expensive lookups or string comparisons are needed.
Restricting dynamic_cast to polymorphic types also makes sense from a logical point of view.
That is, if an object has no virtual functions, it cannot safely be manipulated without knowledge of its exact type. Consequently, care should be taken not to get such an object into a context in which
its type isn’t known. If its type is known, we don’t need to use dynamic_cast .
The target type of dynamic_cast need not be polymorphic. This allows us to wrap a concrete type in a polymorphic type, say, for transmission through an object I/O system (§22.2.4), and then "unwrap" the concrete type later. For example:
class Io_obj { // base class for object I/O system
virtual Io_obj∗ clone() = 0;
};
class Io_date : public Date, public Io_obj { };
void f(Io_obj∗ pio)
{
Date∗ pd = dynamic_cast<Date∗>(pio);
// ...
}
A dynamic_cast to void∗ can be used to determine the address of the beginning of an object of polymorphic type. For example:
void g(Ival_box∗ pb, Date∗ pd)
{
void∗ pb2 = dynamic_cast<void∗>(pb); // OK
void∗ pd2 = dynamic_cast<void∗>(pd); // error : Date not polymorphic
}
The object representing a base class, such as Ival_box , in a derived class object is not necessarily the first subobject in that object of the most derived class. So, pb does not necessarily hold the same address as pb2 . Such casts are only useful for interaction with very low-level functions (only such functions deal with void∗ s). There is no dynamic_cast from void∗ (because there would be no way of knowing where to find the vptr ; §22.2.3).
在类级系中,向上转换是一个简单自然的动作,不需要什么复杂的动作,比如说,狗转成动物,不管是
动物=狗;
动物=(动物)狗;
动物=dynamic_cast<动物>(狗);
动物=static_cast<动物>(狗);
效果都是一样的,但是动物是不是狗呢?关键是理解上面的图,如果向下转换,dynamic_cast依据的是虚函数表,也就是说动物必须有虚函数。动物的虚函数指针找虚函数表,根据虚函数表中与下级对象的偏移量找下一级。从中暗示,转换后前后地址非常可能是不一样的。
但是即使不存在虚函数,动物=狗;狗=(狗)动物,即强转是可以的,我没想明白为什么,如果你知道的话,请务必告诉我
对于上面的问题,我画上了删除线,经过思考研究,得到了答案。
对象的转换有两种方式
- 用户进行强转
- 在运行时程序自己转换
对象在内存中的布局是确定的,用户强转,是根据布局进行内存范围的增大或缩小,那么可能发生所谓的上溢或下溢,而程序自己转换,没办法强转,需要凭借一些线索合理的转换,这个线索就是虚函数表。
- 摘录自 intermediate 第643页 ↩