我在代码中发现了(thanks to a StackOverflow comment)安全漏洞:
std::vector<std::unique_ptr<Item>> items;
template<class... TS> Item& create(TS&&... mArgs)
{
auto item(new Item(std::forward<TS>(mArgs)...);
items.emplace_back(item); // Possible exception and memory leak
return *item;
}
基本上,如果Item
抛出,则new
分配原始emplace_back
可能会泄漏内存。
解决方案永远不会使用原始new
,而是在方法正文中使用std::unique_ptr
权限。
std::vector<std::unique_ptr<Item>> items;
template<class... TS> Item& create(TS&&... mArgs)
{
auto item(std::make_unique<Item>(std::forward<TS>(mArgs)...);
items.emplace_back(std::move(item));
return *item; // `item` was moved, this is invalid!
}
正如您所看到的,返回item
无效,因为我必须使用item
移动std::move
以将其置于items
容器中。
我想不出一个需要在另一个变量中存储item
的地址的解决方案。然而,原始(错误)解决方案非常简洁易读。
是否有一种更优雅的方式可以返回移动到容器中的std::unique_ptr
答案 0 :(得分:8)
你可以写:
template<class... TS>
Item& create(TS&&... mArgs)
{
items.emplace_back(std::make_unique<Item>(std::forward<TS>(mArgs)...));
return *items.back();
}
答案 1 :(得分:2)
在emplace之前缓存引用是一个更普遍适用的选项(例如,对于非向量容器):
template<class... TS> Item& create(TS&&... mArgs)
{
auto item = std::make_unique<Item>(std::forward<TS>(mArgs)...);
auto& foo = *item;
items.emplace_back(std::move(item));
return foo; // This *is* valid.
}
答案 2 :(得分:2)
您的问题标记为C ++ 11,其他答案表明make_unique
未提及它是C ++ 14功能。我相信这种C ++ 11方法也可以解决泄漏问题。
#include <vector>
#include <memory>
#include <utility>
#include <iostream>
struct Item
{
int a, b;
Item(int aa, int bb) : a{aa}, b{bb} { }
};
static std::vector<std::unique_ptr<Item>> items;
template <class... Ts> Item& create(Ts&&... args)
{
items.emplace_back(std::unique_ptr<Item>{new Item(std::forward<Ts>(args)...)});
return *items.back();
}
int main()
{
Item& x = create(1, 2);
std::cout << "( " << x.a << ", " << x.b << " )" << std::endl;
}
这应该是安全的,因为在emplace_back()
已经构建之前无法调用unique_ptr<Item>
,所以即使emplace_back()
投出,Item
已经由unique_ptr
管理{1}}。