考虑一下:
#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 ?
}
此代码有效 使用它有什么危险?
答案 0 :(得分:6)
在C和C ++中,它通常是未定义的行为(读作&#34;非法,但允许编译器&#34;)通过指针或引用取消引用一种类型的对象另一种类型的对象(除了通过指向基类的指针访问外)。这称为严格别名规则。
在此处阅读更多内容:What is the strict aliasing rule?
您的代码违反了此规则,方法是通过指向B类型的指针访问A类型的对象。
请注意,编译器通常无法验证您的静态强制转换(在您的情况下,C-cast等同于static_cast
)。如果你不确定对象类型,dynamic_cast
来验证演员在运行时是否合法,而不是仅在编译时检查并允许一些不正确的演员表的static_cast
。 / p>
答案 1 :(得分:1)
危险在于指向A
时,您通常不确定它是B
还是C
派生类实例。
答案 2 :(得分:1)
可能 - 取决于编译器如何为对象安排内存。
C ++是一种语言,它提供了足够多的绳索来悬挂自己和房间里的其他人!
我的观点是你应该尽可能少地使用演员阵容,并且只在极少数情况下使用。
答案 3 :(得分:1)
一个危险是call_some_function
只能调用A
中的函数(或一般的访问成员)。对B
成员的所有访问都将导致在分配的内存之外访问,可能会带来灾难性的后果。
答案 4 :(得分:1)
这是未定义的行为,所以任何事情都可能发生。在实践中,你
只要涉及单一继承,就可以逃脱它,
但总的来说,你不能指望它;调试实现可以,
例如,在B::call_some_function
中生成代码以确保代码
作为this
传递的地址对应于B
类型的对象
实际上存在于程序中。
答案 5 :(得分:1)
实际上只有的代码才能显示才能正常工作。当您真正只有B
(父类)时,通过告诉编译器您有A
来调用未定义的行为。即使使用相同的编译器,它也可能随时中断。
答案 6 :(得分:0)
这种情况就像这样(并且我确定更多):
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;
它会起作用(最有可能),但这是不好的做法,基本上是未定义的代码。