危险的演员
Dangerous Cast?
考虑一下:
#include <iostream>
using namespace std;
class A{
protected:
void some_function(int params)
{
//inside A: do something A related
}
};
class B: public A{
public:
void call_some_function(int params)
{
some_function(params); // Simple call to A::some_function and NOTHING MORE.
}
};
int main(int argc, char *argv[])
{
A* a = new A();
((B*)(a))->call_some_function(20); // Is it OK ?
}
这段代码可以工作使用它有什么危险?
在C和c++中,通过对另一类型对象的指针或引用来解引用一种类型的对象通常是未定义的行为(读作"非法但编译器允许")(少数例外,如通过对基类的指针访问)。这叫做严格混叠规则。
在这里阅读更多:什么是严格的混叠规则?
你的代码违反了这条规则,通过指向类型b的指针访问类型A的对象
请注意,编译器通常无法验证静态强制转换(在您的情况下,c强制转换相当于static_cast
)。如果您不确定对象类型,dynamic_cast
将在运行时验证强制转换是否合法,而static_cast
仅在编译时检查,并允许一些不正确的强制转换。
一个危险是call_some_function
只能在A
中调用函数(或通常访问成员)。对B
成员的所有访问都将导致对已分配内存之外的访问,可能会导致灾难性的后果。
危险的是有一个指向A
的指针,你通常不确定它是B
还是C
的派生类实例。
可能-取决于编译器如何为对象安排内存。
c++是一门足以让你自己和房间里的每个人上吊的语言!
我的观点是,您应该尽可能少地使用强制转换,并且只在极少数情况下使用。
这是未定义的行为,所以任何事情都可能发生。实际上,你只要只涉及单继承,就可以不受影响,但一般来说,你不能指望它;调试实现可以,例如,在B::call_some_function
中生成代码以确保作为this
传递的地址对应于B
类型的对象实际上存在于程序中。
实际上代码只有看起来可以工作。通过告诉编译器您有一个B
,而实际上只有一个A
(父类),您调用了未定义的行为。它可能随时中断,即使使用相同的编译器。
假牙是在这样的场景(我相信还有更多):
class B: public A{
public:
int local_var;
void call_some_function(int params)
{
local_var = 5; // IN HERE.
some_function(params); // Simple call to A::some_function and NOTHING MORE.
}
};
在本例中,它将尝试访问一个未定义的var (local_var)。
:
struct A {
int i1;
int i2;
}
struct B {
int i1;
int i2;
int i3;
}
A a;
(B)a.i1 = 2;
它将工作(很可能),但这是不好的做法,基本上是未定义的代码。
- 既然存在危险,为什么项目要使用-I include开关
- 未初始化的变量有什么危险
- 将字符移出范围的危险
- 在C++的头文件中使用常量并在程序中询问其地址的任何潜在危险
- CRTP - 危险的内存访问?
- 危险指针的内存排序
- 在对象构造期间,将指向尚未构造的子对象的指针传递给另一个子对象的构造函数是否危险?
- 为什么 CWE 认为 rand() 具有潜在危险
- "this"关键字在C++中的实现限制,因为它与危险但功能示例有关
- cppcheck 抱怨危险地使用 c_str(). c_str() 返回的值在此调用后无效
- cpp 检查抱怨危险使用 c_str(). c_str() 返回的值在本次调用后无效,如何解决?
- 使用危险的幻数N
- 派生类的模板类作为函数的参数 - 危险?
- CUDA:来自不同翘曲但相同块的 2 个线程尝试写入相同的共享内存位置:危险?
- 聚合初始化的 C++17 扩展是否使大括号初始化变得危险?
- 从 int 到 longlong 的危险转换:没有警告
- 在模板中存储右值引用的危险
- 这个危险指针示例是否因为 ABA 问题而存在缺陷?
- 是否存在与将数据流式传输到 c++ 异常类相关的任何危险
- EiffelStudio 编译错误:使用 'tempnam' 很危险,最好使用 'mkstemp'