2016-09-05 3 views
0

Я реализовал CSS-подобную селекторную систему, которая может использоваться в каком-либо дереве объектов. Мои селекторы не совсем оптимальны во время создания. Иногда можно заменить более сложный селектор простым, который является другим типом класса.Возвращает новый экземпляр или сам по себе как std :: shared_ptr, а не необработанный указатель

Я добавил этот метод в мой интерфейс селектора:

virtual std::shared_ptr<Selector> optimize(std::shared_ptr<Selector> target) const = 0; 

Но есть проблема - если селектор не может быть оптимизирован в какой-то простой класс, он должен вернуть себя. Например, у меня есть селектор, который поддерживает список или элементы. Если есть только один элемент, этот селектор является избыточным и должен возвращать более простой селектор, в противном случае он должен возвращать себя.

std::shared_ptr<Selector> CSSChainedSelector::optimize() const 
{ 
    if(sub_selectors.size()==1) { 
     return sub_selectors[0]->optimize(); 
    } 
    else if(sub_selectors.empty()) { 
     return std::shared_ptr<Selector>(new InvalidSelector()); 
    } 
    else { 
     //THIS WILL CAUSE MEMORY ERROR IN THE FUTURE!!! 
     return std::shared_ptr<Selector>(this); 
    } 
} 

Вы не должны создавать shared_ptr от этого. Если вы это сделаете, вы вызываете удаление объекта дважды. Общий указатель должен создаваться только из новых экземпляров. Зная это, я должен был изменить свой дизайн, который теперь вроде ума:

std::shared_ptr<Selector> CSSChainedSelector::optimize(std::shared_ptr<Selector> target) const 
{ 
    if(target.get() != this) 
     return target; 
    // this is probably redundant 
    std::shared_ptr<CSSChainedSelector> self = std::dynamic_pointer_cast<CSSChainedSelector>(target); 
    if(self) { 
     if(chain_.length() == 1) { 
      return chain_[0]->optimize(chain_[0]); 
     } 
    } 
    else 
     return target; 
    return target; 
} 

Это требует от меня, чтобы вызвать метод как это:

std::shared_ptr<Selector> selector(new CSSChainedSelector); 
// parsing happens 
    ... some parsing ... 
selector = selector->optimize(selector); 

Это имеет некоторый смысл, поскольку optimize метод может быть только если вы видите класс извне (он не меняет класс). Оптимизации, которые изменяют класс, могут быть выполнены во время разбора.

Но есть ли способ безопасно вернуть общий указатель на собственный экземпляр класса?

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

+1

[std :: enable_shared_from_this] (http://en.cppreference.com/w/cpp/memory/enable_shared_from_this) –

+0

@MohamadElghawi Я не уверен, насколько я понимаю документы ... Является ли это тогда * безопасным * to 'return std :: shared_ptr (это);', строки не привязаны? –

+0

@ TomášZato не нравится. –

ответ

4
#include <memory> 

struct selector : std::enable_shared_from_this<selector> 
{ 

    std::shared_ptr<selector> optimise() 
    { 

    // the case where we can't optimise, we return ourselves. 
    return this->shared_from_this(); 
    } 
}; 

обновление: простите меня, ваша функция оптимизации const. Если вы возвращающая shared_ptr к себе он должен поэтому быть shared_ptr к сопзЬ:

#include <memory> 

struct selector : std::enable_shared_from_this<selector> 
{ 

    std::shared_ptr<const selector> optimise() const 
    { 

    // the case where we can't optimise, we return ourselves. 
    return this->shared_from_this(); 
    } 
}; 

, если вы не хотите, чтобы вернуть копию, но я думаю, что, вероятно, побеждает объект упражнения.

+0

Хорошая точка с 'const'. Я предполагаю, что когда-то разобрался, я обычно хочу рассматривать селекторы как константы. –

+0

@ TomášZato: Обратите внимание, что экземпляр 'this' должен быть создан с помощью' std :: make_shared' (или эквивалентного). – Jarod42

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