c语言dynamic变量,C++向下转型的陷阱与dynamic_cast运算符

在《C++向上转型》和《C++多态的概念以及用途》两节中讲到,不管是否存在多态,都可以将基类指针指向派生类对象,这称为向上转型(Upcasting)。而反过来,用派生类指针指向基类对象,称为向下转型(Downcasting)。

向下转型的陷阱

向上转型不会有任何问题,而向下转型默认是不允许的,请看下面的代码:

#include

using namespace std;

class Base{ };

class Derived: public Base{ };

int main(){

Base *p1 = new Derived; //向上转型

Derived *p2 = new Base; //Compile Error,向下转型

return 0;

}

如果你一定要向下转型,可以使用强制类型转换,将基类指针强制转换为派生类指针,就不会发生编译错误:

Derived *p2 = (Derived*)new Base;

但这种做法有时候会带来其他问题,请看下面的代码:

#include

using namespace std;

//基类A

class A{

public:

A(int a1, int a2);

void display();

protected:

int m_a1;

int m_a2;

} ;

A::A(int a1, int a2): m_a1(a1), m_a2(a2){ }

void A::display(){ cout<

//派生类B

class B: public A{

public:

B(int a1, int a2, int b);

void display();

private:

int m_b;

};

B::B(int a1, int a2, int b): A(a1, a2), m_b(b){ }

void B::display(){ cout<

int main(){

B *p = (B*)new A(1, 2);

p -> display();

return 0;

}

运行结果:

1, 2, 1947472398

本例中没有虚函数,不存在多态,编译器会根据指针 p 的类型去调用 display() 函数,属于静态绑定。很明显,p 是 B 类的指针,p -> display();最终调用的也是 B 类的函数;不过 p 指向的是 A 类对象,所以p -> display();使用的也是 A 类的成员。

那么现在问题来了,B::display()需要访问 m_b 成员变量,但是 A 类对象不包含 m_b 成员变量,怎么办呢?

我们先来看一下 A 类对象的内存模型:

8ef502ddaafb6ac35fe2a2048c7e616b.png

当调用 display() 函数时,this 指针指向 A 类对象,A 类对象不包含成员变量 b,而编译器不管这些,它不知道这个对象是 A 的还是 B 的,也不知道这个对象包含哪些成员,它只管 this,this 指向哪里就从哪里取值。读取 m_b 时,可以根据 B 类的信息计算出 m_b 的偏移是 8,那么 b 的地址就是(int)this + 8,显然是到 A 类对象的范围之外取值,而这个值一般是垃圾值,没有实际的意义,所以上面的输出结果中会出现1947472398这个奇怪的数字。

dynamic_cast运算符

从上例可以看出,强制向下转型是不安全的,并且在编译期间不容易发现隐患,所以实际开发中很少这样使用,这里仅仅作为教学演示。当继承关系比较复杂,或者搞不清继承关系时,可以使用 dynamic_cast 运算符来保证转型的安全。

dynamic_cast 运算符只允许向上转型,而不允许向下转型。dynamic_cast 只能用在多态中(也就是有虚函数的类),因为它要遍历继承链,确定两个类的“父子关系“。

请看下面的例子:

#include

using namespace std;

class A{

public:

virtual void display(){ cout<

};

class B: public A{

public:

void display(){ cout<

};

int main(){

B *p1 = dynamic_cast(new A);

if(p1){

p1->display();

}else{

cout<

}

A *p2 = dynamic_cast(new B);

if(p2){

p2->display();

}else{

cout<

}

return 0;

}

运行结果:

Base to Derived is error!

B::display()

dynamic_cast 的使用语法为:

dynamic_cast(变量或表达式);

目标类型只能是指针或引用,下面的用法是错误的:

B obj;

dynamic_cast(obj);

dynamic_cast 转换成功后会返回对象的指针或引用,失败则返回 null,所以可以通过 if 来判断是否转型成功。

读者注意:dynamic_cast 的内部实现要依赖于 RTTI,并且会通过 for 循环来遍历继承链,非常低效,能不用则不用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值