2016-01-30 2 views
3

Я понимаю, почему shared_from_this не работает в конструкторе. Я нашел несколько сообщений здесь clearly, объясняя почему. Я также видел для него workaround. Моя проблема связана с этим, но я ищу элегантное обходное решение.Проблемы с shared_from_this в конструкторе для цепочки вызовов

Я очень нравится этот вид дизайна

class Foo : public enable_shared_from_this<Foo> { 
public: 
    typedef shared_ptr<Foo> Ptr; 

    Ptr setA(int) {/* .... */ return this->shared_from_this(); } 
    Ptr setB(int) {/* .... */ return this->shared_from_this(); } 
    Ptr setC(int) {/* .... */ return this->shared_from_this(); } 
}; 

так что я могу Шлейфовое так, что я нахожу очень читаемый

Foo::Ptr foo = make_shared<Foo>(); 
foo->setA(3)->setB(4)->setC(5); 

Однако мои методы Setx делать больше, чем просто присвоить значение к собственности, они могут делать несколько более сложные вещи (например, устанавливать другие свойства и т. д.). Поэтому я хотел бы использовать их в своем конструкторе. т.е.

Foo::Foo(int a, int b, int c) { 
    setA(a); 
    setB(b); 
    setC(c); 
} 

Конечно, это сбой. Однако в моем случае мне не нужен общий этот указатель в моем конструкторе. Это всего лишь побочный эффект моего желания в конце концов.

Загрязненное исправление может быть я просто создаю кучу частных методов setX_, которые выполняют фактическое назначение и другие задачи, но ничего не возвращают. Конструктор вызывает их. Затем у меня есть общедоступные методы setX, просто вызывающие частные, а затем возвращают -> shared_from_this(). Это безопасно, но немного PITA. Можно ли проверить, существует ли общий указатель на без сбоев? т.е.

class Foo : public enable_shared_from_this<Foo> { 
public: 
    typedef shared_ptr<Foo> Ptr; 

     Ptr setA(int) { /*.....*/ return getThis(); } 

protected: 
    Ptr getThis() { 
     return safe_to_get_this ? this->shared_from_this : Ptr(); 
    } 

}; 

UPDATE

я должен был бы сделать это ясно в моей должности, извинения за не делать этого. Причина, по которой я использую интеллектуальные указатели, - это не так, я могу сделать цепочку. Обычно я использую ссылки для этого, если позволяет ситуация.

Но в этом случае Foo (и другие классы) относятся к структуре, подобной графу, они имеют зависимости друг от друга, множественные отношения между родителями и дочерними элементами и т. Д. Поэтому я использую сочетание между общими и слабыми указателями. Но мне нужны методы для возврата shared_ptrs, поэтому я могу использовать слабый или проверять на нуль, если это необходимо (они создаются с использованием фабричных методов, и есть методы find/get, которые нуждаются в проверке на null). В основном это физическая система с различными типами частиц и ограничениями между частицами. Я изначально написал это с моей собственной реализацией умных указателей много лет назад, и теперь я обновляю его до C++ 11. Я мог бы заставить методы setX возвращать ссылки и иметь один метод getThis(), который возвращает общий указатель (это то, что я делал раньше), но потом я беспокоюсь, что он немного несовместим с некоторыми методами, возвращающими интеллектуальные указатели, возвращающие другие Рекомендации.

Foo::Ptr foo = World::create(); 
foo->setA().setB().setC(); 

foo = World::find(...); 
if(foo) foo->setA().setB(); 

Я просто предпочитаю согласованность всегда возвращающих указателей, в отличие от иногда указателей и иногда ссылок. Если для этого нет строгого руководства, которое можно кратко изложить.

UPDATE2

Это (не очень элегантно) обходной путь я использую в настоящее время.Мне просто интересно, есть ли способ сделать это без ручного отслеживания _isInited;

class Foo : public enable_shared_from_this<Foo> { 
public: 
    typedef shared_ptr<Foo> Ptr; 

    Foo() { 
    _isInited = false; 
    setA(); 
    setB(); 
    setC(); 
    _isInited = true; 
    } 

    Ptr setA(int) {/* .... */ return getThis(); } 
    Ptr setB(int) {/* .... */ return getThis(); } 
    Ptr setC(int) {/* .... */ return getThis(); } 

protected: 
    bool _isInited; 

    Ptr() { 
     return _isInited ? this->shared_from_this() : Ptr(); 
    } 
}; 
+2

Если вы просто хотите объединить вызовы функций членов, зачем использовать указатели для начала? Просто верните ссылку на сам класс, и вам не нужно беспокоиться о 'shared_from_this' и т. Д. И я рекомендую вам взглянуть на интеллектуальные указатели с точки зрения * ресурса * вместо простых указателей, которые автоматически удаляются. –

ответ

2

Я хочу добавить правильный комментарий от @JoachimPileborg. Я не думаю, что ваша проблема действительно связана с техническими аспектами аспекта , как использовать enable_shared_from_this, а скорее , когда использовать (умные) указатели.

Рассмотрим следующий код, показывающий два способа использования прикованных вызовов:

struct ptr_foo                                
{ 
    ptr_foo *set_x(int x) { return this; } 
    ptr_foo *set_y(int y) { return this; } 
}; 


struct ref_foo 
{ 
    ref_foo &set_x(int x) { return *this; } 
    ref_foo &set_y(int y) { return *this; } 
}; 


int main() 
{ 
    ptr_foo pf; 
    pf.set_x(0)->set_y(1); 

    ref_foo rf; 
    rf.set_x(0).set_y(1); 

    return 0; 
} 

Первый класс, ptr_foo, использует сырые указатели, а второй один, ref_foo, использует ссылки.


Начнем с того, второй вызов,

rf.set_x(0).set_y(1); 

мне кажется гораздо более последовательным, чем первый,

pf.set_x(0)->set_y(1); 

для общего случая объектов стека на основе.


Кроме того, даже если вы предпочитаете первый, я не думаю, что есть какая-либо причина, чтобы вернуть смарт-указатель, а

  1. выделение средств не происходит

  2. в протокол не предназначен для получения указателя хранилища ptr_foo путем вызова set_?; если кто-то это делает, это злоупотребление протоколом, поскольку оно предназначено только для прикованных вызовов.

Что касается пункта 2, вы никак не можете закрыть протокольные злоупотребления. Например, рассмотреть

ptr_foo pf; 
ptr_foo *ppf = &pf; 

Независимо от того, как вы проектируете ptr_foo, теперь пользователь не содержит указатель на объект такого типа, который не был выделен динамически, и которые могут быть ошибочно удалены.

+0

Да, я согласен с тем, что вы говорите, но ради краткости я опустил информацию из моего вопроса о том, почему я хочу использовать интеллектуальные указатели. Я сейчас обновил этот вопрос. – memo

Смежные вопросы