C++动态转换

本文详细介绍了C++中的dynamic_cast操作符,用于在运行时进行安全的类型转换,特别是在类层次结构中。文章通过实例展示了dynamic_cast在上行转换和下行转换中的用法,强调了它在下行转换时的安全性和必要条件(公有继承和虚函数)。此外,还提到了void指针的转换以及如何在物品分类程序中应用dynamic_cast进行对象分组和总价计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

用法: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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值