我在最新版本的 Xcode(撰写本文时为 9.4.1)中构建了一个 C++ 框架,我再次在 Xcode 中从 Objective-C++ 代码中使用它。我需要执行从一种指针类型到另一种指针类型的 dynamic_cast
。但是,dynamic_cast
仅适用于调试版本,不适用于发布版本。关于 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;
}
在 Debug 和 Release 中,我希望这段代码打印“True”。然而,实际上,Release 模式打印“False”。作为冒烟测试,位于 this SO post 的 dynamic_cast
工作得很好。
有趣的是,同样类型的代码在 C++ 命令行应用程序中工作,同样在 Xcode 中。我曾尝试在 Release模式下禁用优化器,但这似乎无法解决问题。
我有一个示例项目 up on GitHub here .记得在Release里编译一下看看我问题的原因。我已经包含了用于 Objective-C++ 的 TestCast
方案,以及用于直接 C++ 的 TestCastCPP
方案。
最佳答案
很难知道编译器的具体细节,因为编译器如何执行 RTTI 具有一定的灵 active (即,规范没有详细说明)。
在这种情况下,由于 Child 类没有定义任何虚函数,我怀疑编译器为 Child 类的每个翻译单元发出了 RTTI。
当链接框架和链接可执行文件时,每个都有自己的子 RTTI 信息,因为每个翻译单元都会发出自己的 RTTI。
我怀疑其中一个的父链接与另一个的父链接不匹配,因此它们没有相同的父指针,并且动态加载器没有“修复”这些东西。 (dynamic_cast<Child*>
基本上遍历父指针链,直到它通过指针值而不是 RTTI 值找到匹配项。)
如果你看过 nm -g TestCast | c++filt
在应用程序和框架的转储中,您可以看到 RTTI block 。拆解它们,我认为 Child RTTI 在这两种情况下都已经解析为它们自己的 Parent RTTI。
为什么它适用于 DEBUG 而不是 RELEASE?发布优化之一可能是根据使用情况去除外部链接符号的死代码。因此 DEBUG 的动态加载器 (dyld) 能够解析符号,但 RELEASE 构建一个或多个符号已经在内部解析。
可能有一种方法可以指示应该保留和导出 RTTI 的“未使用”符号,这将因编译器/链接器而异。但这比提供避免该问题的显式“第一个虚拟函数”(例如虚拟 Child 析构函数)更麻烦。
关于c++ - 为什么这个来自Objective-C++的dynamic_cast调试成功但发布失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50929670/