请参阅以下代码:
#define mPtr (static_cast<Dog*> (new(Animal)))
class Animal{
public:
virtual void talk(){cout<<"A";}
};
class Dog:public Animal{
int test;
public:
void talk(){bark();}
void bark(){
test=0;cout<<test;
}
};
int main() {
mPtr->talk();
mPtr->bark();
return 0;
}
输出:
A0
基类talk()正在执行是有效还是未定义的行为?
答案 0 :(得分:6)
这是直接未定义的行为。当您100%确定指针指向该派生类的对象时,您只能使用static_cast
强制转换为派生类。对于类层次结构中的安全转换,可以使用dynamic_cast
。在这种情况下,dynamic_cast<Dog*>(new Animal)
将返回nullptr
,表示指向的对象不属于Dog
类。
答案 1 :(得分:3)
来吧,让我们解压缩代码并说明错误原因。将你的罪孽隐藏在一个宏观中并不会使它们消失。
int main() {
Animal a;
Dog *dog = &a;
dog->talk();
}
试试吧。它编译?如果没有,编译器会说什么?如果编译器说分配没有意义,为什么强迫分配呢?
转发是安全的唯一情况是您知道动态类型是正确的(来自上下文,或使用typeid
或其他),或者您使用{{1测试你的对象是否具有预期的子类型。
您未经检查的向下转换为错误的子类型确实是未定义的行为。
好的,我看到混淆实际上是dynamic_cast
的含义,而不是多态性。那么,让我们看一下documentation。
static_cast
您的代码(向下转发)是描述的第二种情况:
2)如果new_type是指向某个类D的指针或引用,并且表达式的类型是指向其非虚拟基数B的指针或引用,则static_cast执行向下转换。 ... 这样的static_cast不进行运行时检查以确保对象的运行时类型实际上是D ,并且只有在通过其他方式保证此前提条件时才可以安全使用
粗体部分(我的格式)是您的演员非法的原因。
有用的是要注意这只是因为你正在构建一个指针(尽管它对于引用是相同的) - 指针不是对象。转换指针不会改变对象或创建新对象,它只是在现有对象上提供不同的视图。
相反,请参阅第一个案例:
1)如果存在从表达式到new_type的隐式转换序列,或者如果从表达式直接初始化对象或类型new_type的引用的重载解析将找到至少一个可行的函数,则
static_cast < new_type > ( expression )
返回假想变量static_cast<new_type>(expression)
初始化为Temp
所以,如果你添加了一个构造函数
new_type Temp(expression);
从动物创建一个Dog ,你可以写
Dog::Dog(Animal const &) {}
并且没问题 - 这会从Animal a;
Dog dog = static_cast<Dog&>(a);
创建一个狗(与a
不同的对象)。