我有一个用最新版本的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 post的dynamic_cast
可以正常工作。
我有一个示例项目up on GitHub here。记得在Release中编译它,以查看我提出问题的原因。我已经为Objective-C ++引入了TestCast
方案,为直接C ++引入了TestCastCPP
方案。
答案 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析构函数)避免问题更麻烦。