4

我发现(感谢 StackOverflow 评论)我的代码中有一个安全漏洞:

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,使用 raw分配new可能会泄漏内存。emplace_back

解决方案从不使用 raw new,而是std::unique_ptr在方法主体中使用 right 。

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被移动以放置在容器中的东西?

4

3 回答 3

8

你可以写:

template<class... TS>
Item& create(TS&&... mArgs)
{
    items.emplace_back(std::make_unique<Item>(std::forward<TS>(mArgs)...));
    return *items.back();
}
于 2014-02-20T17:29:43.757 回答
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()在 a 已经构造之前不能调用unique_ptr<Item>,所以即使emplace_back()抛出,你Item已经由unique_ptr.

于 2014-02-20T18:04:43.853 回答
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.
}
于 2014-02-20T17:36:00.890 回答