2013-09-01 2 views
3

Я едва использовал функцию перемещения на C++, поэтому я не уверен, что я делаю правильно. Я был бы рад, если бы кто-нибудь посмотрел мой код и указал на любые ошибки, которые я сделал.Правильно ли это использовать ссылки на перемещение и unique_ptr?

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

Кроме того, нужен ли в моем классе определение конструктора и деструктора?

Спасибо.

#define TYPE(x) std::identity<decltype(x)>::type 

namespace General 
{ 
    template<class T> 
    std::string ToString(const T& x) 
    { 
     std::ostringstream ss; 
     ss << x; 
     return ss.str(); 
    } 
} 

namespace General 
{ 
    template<class T, class KEY = std::string> 
    class ResourceManager 
    { 
    public: 
     typedef T ResourceType; 
     typedef KEY KeyType; 

     void Load(const KeyType& key, std::unique_ptr<ResourceType>&& resource); 
     const ResourceType& Read(const KeyType& key) const; 
     ResourceType& Modify(const KeyType& key); 
     void Unload(const KeyType& key); 
     std::unique_ptr<ResourceType>&& Release(const KeyType& key); 
     void UnloadAll(); 

    private: 
     std::map<KeyType, std::unique_ptr<ResourceType>> data; 
    }; 
} 

template<class T, class KEY> 
void General::ResourceManager<T, KEY>::Load(const KeyType& key, std::unique_ptr<ResourceType>&& resource) 
{ 
    auto find_it = data.lower_bound(key); 
    if (find_it != data.end() && ! (data.key_comp()(key, find_it->first))) 
    { 
     throw std::runtime_error(General::ToString(key) + " already exists!"); 
    } 
    else 
    { 
     data.insert(find_it, TYPE(data)::value_type(key, std::move(resource))); 
    } 
} 

template<class T, class KEY> 
const typename General::ResourceManager<T, KEY>::ResourceType& General::ResourceManager<T, KEY>::Read(const KeyType& key) const 
{ 
    auto find_it = data.find(key); 
    if (find_it == data.end()) 
    { 
     throw std::runtime_error(General::ToString(key) + " could not be found!"); 
    } 
    else 
    { 
     return *find_it->second; 
    } 
} 

template<class T, class KEY> 
typename General::ResourceManager<T, KEY>::ResourceType& General::ResourceManager<T, KEY>::Modify(const KeyType& key) 
{ 
    auto find_it = data.find(key); 
    if (find_it == data.end()) 
    { 
     throw std::runtime_error(General::ToString(key) + " could not be found!"); 
    } 
    else 
    { 
     return *find_it->second; 
    } 
} 

template<class T, class KEY> 
void General::ResourceManager<T, KEY>::Unload(const KeyType& key) 
{ 
    auto find_it = data.find(key); 
    if (find_it == data.end()) 
    { 
     throw std::runtime_error(General::ToString(key) + " could not be found!"); 
    } 
    else 
    { 
     data.erase(find_it); 
    } 
} 

template<class T, class KEY> 
std::unique_ptr<typename General::ResourceManager<T, KEY>::ResourceType>&& General::ResourceManager<T, KEY>::Release(const KeyType& key) 
{ 
    auto find_it = data.find(key); 
    if (find_it == data.end()) 
    { 
     throw std::runtime_error(General::ToString(key) + " could not be found!"); 
    } 
    else 
    { 
     auto resource = std::move(find_it->second); 
     data.erase(find_it); 
     return std::move(resource); 
    } 
} 

template<class T, class KEY> 
void General::ResourceManager<T, KEY>::UnloadAll() 
{ 
    data.clear(); 
} 

ответ

3

Вот упрощенный фрагмент кода, который демонстрирует суть вашей ситуации, и как писать код идиоматически:

std::map<int, std::unique_ptr<Foo>> m; 

void add_to_map(int key, std::unique_ptr<Foo> val) 
{ 
    m[key] = std::move(val); 
} 

Использование:

add_to_map(1, std::unique_ptr<Foo>(new Foo(1, 2, 3))); 

std::unique_ptr<Foo> p(new Foo(true, 'x')); 
p->mutate(); 
add_to_map(std::move(p)); 

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


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

void add_maybe(std::unique_ptr<Foo> & val) 
{ 
    if (rand() % 2 == 0) 
    { 
     m[12] = std::move(val); 
    } 
} 

Использование:

std::unique_ptr<Foo> p(new Foo(true, 'x')); 

add_maybe(p); 

if (p) { /* we still own the resource   */ } 
else { /* the resource is now owned by the map */ } 

Update: Для того, чтобы освободить объект с карты, возврат по значению:

std::unique_ptr<Foo> release(int key) 
{ 
    auto it = m.find(key); 
    return it == m.end() ? { } : std::move(it->second); 
} 
+0

Значит, вызывающий функции должен явно перемещать свой объект к моему параметру значения? –

+1

@NeilKirk: Если у вызывающего есть lvalue, то да. В противном случае это неявно, и дополнительный код не нужен (как в первом примере использования). И этот пример должен быть 'add_to_map (std :: make_unique (1,2,3));' действительно, хотя для этого требуется C++ 14. –

+0

Как насчет типа возврата функции Release? –

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