Ссылочный тип C++ как переменная экземпляра в Objective-C++

Ссылочные типы C++ в качестве переменных экземпляра запрещены в Objective-C++. Как я могу обойти это?


person Karl von Moor    schedule 30.04.2010    source источник


Ответы (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
person Georg Fritzsche    schedule 30.04.2010
comment
Я не понимаю, почему вы просто не храните указатель на сам SomeClass. Разница между ссылкой и указателем (в основном) заключается в семантике, которую ваш интерфейс отлично передает. Смотрите мой ответ. - person Sebastian; 24.08.2015
comment
@Sebastian Это спорно, если вы начинаете только с одним участником (как часто у вас только один участник?). Это становится интересным, если вы инициализируете более чем одного члена и хотите вернуть правильное детерминированное поведение. FWIW, я упомянул указатели и этот подход как возможные варианты. - person Georg Fritzsche; 24.08.2015
comment
В чем проблема хранить несколько указателей вместо ссылок? В чем проблема с детерминизмом? - person Sebastian; 24.08.2015
comment
@Sebastian Если у вас есть N объектов C++, которыми владеет ваша оболочка ObjC, собираетесь ли вы удалить/деинициализировать/... каждый из них вручную? - person Georg Fritzsche; 24.08.2015
comment
@GeorgFritzsche Вероятно, нет. Но тут все равно не так. A не владеет исх. Вы создали необходимость самостоятельно реализовать Dealloc и, по крайней мере, решили проблему более сложную, чем та, о которой задавался вопрос. - person Sebastian; 25.08.2015
comment
И если A владел N объектами C++, почему бы не сохранить их как структуру или unique_ptr и установить GCC_OBJC_CALL_CXX_CDTORS ? - person Sebastian; 25.08.2015
comment
@Sebastian В данном примере это не так, но обычно с такими вопросами возникает более широкая основная проблема, поэтому я предложил два варианта. Когда это было написано, CXX_DTORS по некоторым причинам не был полезен в наших более крупных рабочих проектах - известно ли сейчас, что он надежен? - person Georg Fritzsche; 26.08.2015
comment
Я начинаю понимать, откуда ты. Может быть, я просто пропустил этот контекст в вашем ответе. Мы поставили его сейчас :-) - person Sebastian; 27.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.

person Sebastian    schedule 24.08.2015
comment
Утверждение в каждом члене A кажется громоздким и подверженным ошибкам, если вы можете предпочтительнее использовать шаблоны, требующие подтверждения/проверки только один раз. - person Georg Fritzsche; 24.08.2015
comment
Это ничем не отличается от утверждения, что ваш указатель pimpl все еще действителен в каждом элементе. Может быть, это слишком параноидально, но это относится к обоим решениям. - person Sebastian; 24.08.2015

boost::ref() может помочь?

person Edward Strange    schedule 30.04.2010
comment
Не совсем так - 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).

person Sterling Archer    schedule 01.04.2014
comment
Почему бы не хранить (возможно, умный) указатель на экземпляр прямо здесь? Я не думаю, что обертка вам что-то даст в этом сценарии. - person Georg Fritzsche; 02.04.2014
comment
Почему бы и нет. Если подумать, в таких простых ситуациях, как в примере, я обычно просто сохранял указатель. - person Sterling Archer; 02.04.2014