静态铸造工作和动态铸造分段故障



下面的代码可以正常编译和工作:

#include<iostream>
class Base {
protected:
int _a;
public: 
virtual ~Base()=default;
Base(int a) : _a{a} {};
int getit() const { return _a; }
};
class Derived : public Base {
public:
Derived(int a) : Base{a} {};
int get2() const { return 2*this->_a; }
};
int main() {
Base D{2};
auto* ptr = &D;
auto* ptr2 = static_cast<Derived*>(ptr);

std::cout << ptr2->get2() << std::endl;
}

输出
4

如果我把static_cast换成dynamic_cast,它会出现分段故障。

我的问题:使用static_cast转换为派生类不添加任何数据成员是安全的吗?

使用static_cast向下强制转换为派生类安全吗

仅当对象的动态类型是派生类(或者派生类是进一步派生的动态类型的基类)。向上强制转换也是安全的,但这种转换也隐式地工作。

如果你不知道这是否是这种情况-就像在例子中一样-当你知道不是这种情况时,这绝对是不安全的,并且示例程序的行为是未定义的。使用dynamic_cast是安全的,但不检查它是否返回null,然后间接通过该null是不安全的。

总之,这是安全的:

if (auto* ptr2 = dynamic_cast<Derived*>(ptr)) {
std::cout << ptr2->get2() << std::endl;
} else {
std::cout << "It's not Derived :(" << std::endl;
}

也就是说,如果你认为你需要dynamic_cast,那么你可能应该重新考虑设计。您可能应该使用虚函数。例子:

class Base {
protected:
int _a;
public: 
virtual ~Base()=default;
Base(int a) : _a{a} {};
virtual int getit() const { return _a; }
};
class Derived : public Base {
public:
using Base::Base;
int getit() const override { return 2*this->_a; }
};

在这种情况下,使用错误类型的强制转换会给您带来麻烦。正如在评论中提到的,ptr2没有指向一个有效的Derived对象。

所以,你的static_cast,你看到未定义的行为;这只是碰巧给出了一个明显的"正确"答案,但你永远不能依赖它。

然而,使用dynamic_cast将导致ptr2具有nullptr的值(因为ptr指向的对象不是Derived);你试图解引用nullptr导致你的程序崩溃(因为它经常会)。

但是请注意,使用dynamic_cast将允许您轻松检查转换是否成功(通过测试nullptr的返回值);static_cast没有给你这个选项。

最新更新