具有基类属性是否有效,即使在完成向下转换时它是虚拟的

时间:2018-01-22 10:47:46

标签: c++ oop c++11

请参阅以下代码:

#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()正在执行是有效还是未定义的行为?

2 个答案:

答案 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 不同的对象)。