Objective-C++ 中禁止将 C++ 引用类型作为实例变量。我该如何解决这个问题?
4 回答
您不能明智地将引用用作实例变量,因为无法初始化实例变量并且无法重新定位引用。
另一种方法可能是简单地使用(可能是智能的)指针。
另一种使您更接近 C++ 行为的可能性是为您的 C++ 成员使用 PIMPL 样式的成员:
struct CppImpl {
SomeClass& ref;
CppImpl(SomeClass& ref) : ref(ref) {}
};
@interface A : NSObject {
CppImpl* pimpl;
}
- (id)initWithRef:(SomeClass&)ref;
@end
@implementation
- (id)initWithRef:(SomeClass&)ref {
if(self = [super init]) {
pimpl = new CppImpl(ref);
}
return self;
}
// clean up CppImpl in dealloc etc. ...
@end
Georg 的第一句话是完全正确的:
您不能明智地将引用用作实例变量,因为无法初始化实例变量并且无法重新定位引用。
但我不认为他的解决方案是最好的。
指针和引用之间的语义差异很小。引用本质上是一个不能为空的指针。因此,在您的界面中使用引用来明确 anullptr
不是有效的初始化器参数当然是一个好主意。但在内部,您可以简单地存储一个指针:
@interface A : NSObject {
SomeClass* p;
}
- (id)initWithRef:(SomeClass&)ref;
@end
@implementation A
- (id)initWithRef:(SomeClass&)ref {
if(self = [super init]) {
p = &ref;
}
return self;
}
@end
没有更多(在最坏的情况下:手动)内存分配,根本没有资源处理,没有额外的间接等。 A 的每个成员都可以简单地断言p != nullptr
.
boost::ref() 可能有帮助吗?
更通用的解决方案是使用reference_wrapper<T>
而不是自定义结构。最终结果是相似的。
再说一次,如果您只需要存储一个成员,那么通过使用 struct 或此包装器,您不会获得比指针更多的优势。(感谢乔治!)
我以 Georg 的回答作为示例的起点:
// This bare interface can be used from regular Objective-C code,
// useful to pass around as an opaque handle
@interface A : NSObject
@end
// This interface can be shown to appropriate Objective-C++ code
@interface A (Private) // @interface A () if it's just for this class's .mm file
- (id)initWithRef:(SomeClass &)ref;
@property (readonly, nonatomic) SomeClass &ref;
@end
@implementation A {
reference_wrapper<SomeClass> *_refWrapper;
}
- (id)init {
// and/or throw an exception
return nil;
}
- (id)initWithRef:(SomeClass &)ref {
self = [super init];
if(self) {
_refWrapper = new reference_wrapper<SomeClass>(ref);
}
return self;
}
- (SomeClass &)ref {
// reference_wrapper<T> is implicitly convertible to T&
return *_refWrapper;
// This would also work:
// return _refWrapper->get();
}
- (void)dealloc {
delete _refWrapper;
}
@end
这种多头模式有助于在 Objective-C 代码中传递不透明的句柄,同时为少数人提供 Objective-C++ 功能(即使它只是那个 objc 类的实现)。