为什么来自Objective-C ++的dynamic_cast在调试中成功但在发布中失败?

时间:2018-06-19 13:26:03

标签: c++ objective-c++

我有一个用最新版本的Xcode(在撰写本文时为9.4.1)构建的C ++框架,该框架是从Objective-C ++代码(也是在Xcode中)使用的。我需要从一种指针类型执行另一种dynamic_cast。但是,dynamic_cast仅在Debug版本中有效,而在Release版本中无效。我是否缺少某些关于dynamic_cast在Objective-C ++中如何使此示例失败的信息?

C ++框架

TestClass.hpp

class Parent {
    public:
    // https://stackoverflow.com/a/8470002/3938401
    // must have at least 1 virtual function for RTTI
    virtual ~Parent();

    Parent() {}
};

class Child : public Parent {
public:
    // if you put the implementation for this func 
    // in the header, everything works.
    static Child* createRawPtr(); 
};

TestClass.cpp

#include "TestClass.hpp"

Parent::~Parent() {}

Child* Child::createRawPtr() {
    return new Child;
}

Objective-C ++命令行应用

main.mm

#import <Foundation/Foundation.h>
#import <TestCastCPP/TestClass.hpp>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Parent *parentPtr = Child::createRawPtr();
        Child *child = dynamic_cast<Child*>(parentPtr);
        NSLog(@"Was the cast successful? %s", child != nullptr ? "True" : "False");
    }
    return 0;
}

在调试和发行版中,我都希望此代码显示“ True”。但是,实际上,发布模式会打印“ False”。作为烟雾测试,this SO postdynamic_cast可以正常工作。

有趣的是,同样的代码也可以在Xcode中的C ++命令行应用程序中工作。我曾尝试在发布模式下禁用优化器,但这似乎无法解决问题。

我有一个示例项目up on GitHub here。记得在Release中编译它,以查看我提出问题的原因。我已经为Objective-C ++引入了TestCast方案,为直接C ++引入了TestCastCPP方案。

1 个答案:

答案 0 :(得分:2)

很难知道编译器的细节,因为编译器如何进行RTTI具有一定的灵活性(即,规范未对其进行详细说明)。

在这种情况下,由于Child类没有定义任何虚函数,因此我怀疑编译器在Child类的每个转换单元中都发出了RTTI。

当链接框架和链接可执行文件时,每个组件都有自己的子RTTI信息,因为每个翻译单元都发出自己的RTTI。

我怀疑一个链接的父链接与另一个链接的父链接不匹配,因此它们没有相同的父指针,并且那些东西没有被动态加载器“修复”。 (dynamic_cast<Child*>基本上沿着父指针链移动,直到找到按指针值而不是RTTI值匹配的对象。)

如果您查看了应用程序和框架的nm -g TestCast | c++filt转储,则可以看到RTTI块。拆解它们,我认为子RTTI在两种情况下都已经解决为他们自己的父RTTI。

为什么它对DEBUG有效,但对RELEASE不起作用?发行优化之一可能是根据使用情况对外部链接符号进行死代码剥离。因此,用于DEBUG的动态加载程序(dyld)能够解析符号,但是RELEASE构建一个或多个符号已在内部解析。

可能有一种方法可以指示并保留和导出RTTI的“未使用”符号,这将因编译器/链接器而异。但这比提供显式的“第一个虚拟函数”(例如虚拟Child析构函数)避免问题更麻烦。