2017-01-09 12 views
0

В духе «выбрать контейнеры с умом», я заинтересован в том, что это лучший способ, чтобы сохранить либо ровно одинилине объекта, например, в качестве члена в классе. Это может иметь место, например, если удерживаемый объект дорог для вычисления и должен каким-то образом кэшироваться (или любого другого типа «позднего» создания).Каков предпочтительный способ хранения одного или любого объекта в C++?

Очевидными кандидатами являются std::vector и std::unique_ptr, например:

class object_t; 
class foo_t { 
    std::unique_ptr<object_t> m_cache; 
    public: 
    object_t getObject() { 
     if(not m_cache) { 
     m_cache.reset(new object_t()); // object creation is expensive 
     } 
     return object_t(*m_cache); 
    } 
}; 

и точно так же с вектором (или почти любой другой контейнер):

class object_t; 
class foo_t { 
    std::vector<object_t> m_cache; 
    public: 
    object_t getObject() { 
     if(m_cache.empty()) { 
     m_cache.push_back(object_t()); // object creation is expensive 
     } 
     return m_cache.front(); 
    } 
}; 

Конечно, есть еще возможность имеют некоторую логическую переменную, которая сохраняет состояние объекта:

class object_t; 
class foo_t { 
    bool cache_healthy; 
    object_t m_cache; 
    public: 
    foo_t() : cache_healthy(false), m_cache() {} 
    object_t getObject() { 
     if(not cache_healthy) { 
     m_cache = object_t(); 
     cache_healthy = true; 
     } 
     return m_cache; 
    } 
    /* do other things that might set cache_healthy to false. */ 
}; 

Из трех примеров, мне нравится последний, тем меньше, потому что он либо создает объект дважды, либо, если я изменяю object_t на «дешевый»/неполный конструктор, может вернуть недопустимый объект.

Решение с вектором, которое мне больше не нравится семантически, потому что вектор (или любой другой тип контейнера) может создать впечатление, что может быть больше одного объекта.

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

+3

'станда: : 'или' boost.optional'. –

+0

спасибо, я не знал об этой функции. Однако это C++ 17, но будет помнить об этом. – jafasi

+1

для меня решение с 'std :: unique_ptr' кажется лучшим,' vector' кажется довольно странным для этой цели - мне было бы страшно, что в какой-то реализации будет использоваться начальный размер не 0. Идея с object_t будет создавать объект дважды - так что это не хорошо - что, если ваш кеш имеет несколько больших полей. необязательное решение - вероятно, будет сильно рекламироваться, но первоначально использует размер вашего объекта кэша - возможно, это не такое идеальное решение - если у вас много кешей, и не все из них используются (и объект очень большой). – marcinj

ответ

1

«Очевидное» решение использует boost::optional или (в C++ 17) std::optional.

Реализации нечто подобное может выглядеть следующим образом:

template <typename T> 
class optional 
{ 
public: 
    optional() : m_isset(false) {} 

    template <typename ...Args> 
    optional(Args... args) { 
     m_isset = true; 
     new (&m_data[0]) optional { args... }; 
    } 

    // overload operator-> and operator* by reinterpret_casting m_data, throwing exceptions if isset == false 

private: 
    bool m_isset; 
    char m_data[sizeof(T)]; 
} 

К недостаткам ваших решений являются ненужным выделением кучи в 1 и 2, и полагаться на копию в 3.

+0

спасибо. Я не знал об этой функции C++ 17. – jafasi

+1

Фактически, размещение-новое является очень старой функцией, оно уже было доступно до стандартизации в 1998 году. – filmor