Ссылочные типы C++ в качестве переменных экземпляра запрещены в Objective-C++. Как я могу обойти это?
Ссылочный тип C++ как переменная экземпляра в Objective-C++
Ответы (4)
Вы не можете разумно использовать ссылки в качестве переменных экземпляра, потому что нет способа инициализировать переменные экземпляра, а ссылки не могут быть переустановлены.
Альтернативой может быть простое использование (возможно, умных) указателей.
Другая возможность, которая приближает вас к поведению, подобному C++, заключается в использовании члена в стиле PIMPL для членов C++:
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
A
не владеет исх. Вы создали необходимость самостоятельно реализовать Dealloc и, по крайней мере, решили проблему более сложную, чем та, о которой задавался вопрос.
- person Sebastian; 25.08.2015
A
владел N объектами C++, почему бы не сохранить их как структуру или unique_ptr
и установить GCC_OBJC_CALL_CXX_CDTORS
?
- person Sebastian; 25.08.2015
CXX_DTORS
по некоторым причинам не был полезен в наших более крупных рабочих проектах - известно ли сейчас, что он надежен?
- person Georg Fritzsche; 26.08.2015
Первое предложение Георга совершенно верно:
Вы не можете разумно использовать ссылки в качестве переменных экземпляра, потому что нет способа инициализировать переменные экземпляра, а ссылки не могут быть переустановлены.
Но я не думаю, что его решение является лучшим.
семантическая разница между указателем и ссылкой невелика. Ссылка — это, по сути, указатель, который не может быть нулевым. Так что, безусловно, хорошей идеей будет использовать ссылки в вашем интерфейсе, чтобы было очень очевидно, что nullptr
не является допустимым аргументом инициализатора. Но внутри вы можете просто сохранить указатель:
@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>
не имеет конструктора по умолчанию, и вы не можете инициализировать переменные экземпляра.
- person Georg Fritzsche; 30.04.2010
Более общее решение — использовать reference_wrapper<T>
вместо пользовательской структуры. Конечный результат аналогичен.
Опять же, если вам нужно сохранить только один элемент, вы не получите большого преимущества перед указателями, используя либо структуру, либо эту оболочку. (Спасибо Георг!)
Я использовал ответ Георга в качестве отправной точки для примера:
// 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).