用法:dynamic_cast < type_name>( expression )
说明:dynamic_cast 操作符,它允许在运行时刻进行类型转换,从而使程序能够在一个类层次结构中安全地转换类型,把基类指针转换成派生类指针,或把指向基类的左值转换成派生类的引用,不能进行值类型的转换。
必须是公有继承,基类要有虚函数。
特点:
1.与C++支持的其他强制转换不同的是,dynamic_cast 是在运行时执行的类型转换。
2.如果针对指针类型的 dynamic_cast 失败,则dynamic_cast 的结果是 nullptr。
3.如果针对引用类型的 dynamic_cast 失败,则 dynamic_cast 会抛出一个异常。
4.在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的。
5.在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
4.1上行转换
class Object
{
int value;
public:
Object(int x=0):value(x){}
~Object(){}
void func()//设置为虚函数效果相同
{
cout<<"value: "<<value<<endl;
}
};
class Base:public Object
{
int num;
public:
Base(int x=0):Object(x+10),num(x){}
~Base(){}
void func()
{
cout<<"num: "<<num<<endl;
}
};
int main()
{
Base base(10);
Object* opa=&base;//ok
Object* opb=dynamic_cast<Object*>(&base);//ok
Object* opc=static_cast<Object*>(&base);//ok
Object& obja=base;//ok
Object& objb=dynamic_cast<Object&>(base);;//ok
Object& objc=static_cast<Object&>(base);//ok
Object&& robjb=dynamic_cast<Object&&>(base);;//ok
Object oba=base;//ok
Object obb=dynamic_cast<Object>(base);//error
Object obc=static_cast<Object>(base);//ok
}
上行转换由于赋值赋值兼容性规则均可以编译通过,效果也均相同且均在编译时进行转换,动态转换不能进行值类型转换。只要是上行转换无论是否有公有继承且有虚函数均在编译期进行转换和静态转换相同。
4.2下行转换
class Object
{
int value;
public:
Object(int x=0):value(x){}
~Object(){}
virtual void func()
{
cout<<"value: "<<value<<endl;
}
};
class Base:public Object
{
int num;
public:
Base(int x=0):Object(x+10),num(x){}
~Base(){}
virtual void func()
{
cout<<"num: "<<num<<endl;
}
};
int main()
{
Base base(10);
Object obj(100);
Object* pobj1=&base;//ok
Base* pa1=dynamic_cast<Base*>(pobj1);//ok,在运行时进行转换
pa1->func();//ok
Base* pb1=static_cast<Base*>(pobj1);//ok,在编译时进行转换
pb1->func();//ok
//将pobj的指向从派生类到基类
Object* pobj2=&obj;//ok
Base* pb2=static_cast<Base*>(pobj2);//ok,在编译时进行转换
pb2->func();//ok
Base* pa2=dynamic_cast<Base*>(pobj2);//ok,在运行时进行转换
pa2->func();//error,此时pa2为空指针,解引用空指针程序崩溃
try//在使用引用进行转换时需要进行测试
{
Base& ba=dynamic_cast<Base&>(*pobj2);//ok,在运行时进行转换
ba.func();//error,抛出异常
}
catch(std::bad_cast& x)
{
cout<<x.what()<<endl;
}
return 0;
}
pa2为空指针的原因是,在动态转换时会检测pobj2所指向的对象的类型和要转换的类型是否相同或者检测要转换的类型是不是其基类,如果相同或者是其基类则转换成功;否则会失败,即会将指针pa2置为空指针,如果用引用接收则会抛出异常。动态转换是通过RTTI(运行时的类型识别信息)来进行检查的。而静态转换并不会进行检查。
总结:基类中必须至少有一个虚函数,否则编译失败。使用异常给程序增加了相应的运行开销,所以尽可能使用引用的dynamic_cast。
4.3void类型指针
int main()
{
Base base;
void* vp=dynamic_cast<void*>(&base);//ok
Object* op=dynamic_cast<Object*>(vp);//error,dynamic_cast的操作数必须是指向完整类类型的指针
}
一个void* 的真实意思是无类型信息。
4.4物品分类程序
class Goods
{
float _weight; // 重量
public:
Goods(float wt) : _weight(wt) {}
virtual ~Goods() { std::cout << "~Trash()" << std::endl; }
float GetWeight() const { return _weight; }
virtual float GetPrice() const = 0; // 价格
};
// 铜
class Copper : public Goods
{
static float price;
public:
Copper(float wt) : Goods(wt) {}
float GetPrice() const { return price; }
static void SetPrice(float newprice)
{
price = newprice;
}
};
float Copper::price = 2.80;
// 纸张
class Paper : public Goods
{
static float price;
public:
Paper(float wt) : Goods(wt) {}
float GetPrice() const { return price; }
static void SetPrice(float newprice)
{
price = newprice;
}
};
float Paper::price = 0.20;
// 玻璃
class Glass : public Goods
{
static float price;
public:
Glass(float wt) : Goods(wt) {}
float GetPrice() const { return price; }
static void SetPrice(float newprice)
{
price = newprice;
}
};
float Glass::price = 0.32;
template<class Container >
void sumPrice(Container& bin)
{
float total = 0;
for (auto p : bin)
{
//cout << typeid(x).name() << endl;
total += p->GetPrice() * p->GetWeight();
cout << "wight of : " << typeid(*p).name() << " = " << p->GetWeight() << endl;
}
cout << "Total price = " << total << endl;
}
int main()
{
srand(time(0)); // Seed the random number generator
vector<Goods*> bin;
int n = rand() % 200; // 0 199
for (int i = 0; i < n; i++)
{
switch (rand() % 3)
{
case 0:
bin.push_back(new Copper((rand() % 1000) / 10.0));
break;
case 1:
bin.push_back(new Paper((rand() % 1000) / 10.0));
break;
case 2:
bin.push_back(new Glass((rand() % 1000) / 10.0));
break;
}
}
// Note: bins hold exact type of object, not base type:
vector<Glass*> glassBin;
vector<Paper*> paperBin;
vector<Copper*> coppBin;
vector<Goods*>::iterator sorter = bin.begin();
// Sort the Trash:
while (sorter != bin.end())
{
Copper* cp = dynamic_cast<Copper*>(*sorter);
Paper* pa = dynamic_cast<Paper*>(*sorter);
Glass* gp = dynamic_cast<Glass*>(*sorter);
if (cp) coppBin.push_back(cp);
else if (pa) paperBin.push_back(pa);
else if (gp) glassBin.push_back(gp);
++sorter;
}
sumPrice(coppBin);
sumPrice(paperBin);
sumPrice(glassBin);
sumPrice(bin);
for (auto& x : bin)
{
delete x;
x = nullptr;
}
return 0;
}