这种方式是否可以接受向向量添加unique_ptr?
Is this way acceptable for adding a unique_ptr to a vector?
假设我有一个盒子。它包含由Box
制造的东西,并且只有Box
。这些东西也是不可复制的。
还有一个所有权关系,所以我想使用unique_ptr
s.(如果盒子走了,一切都随之而来。
#include <memory>
#include <vector>
class Thing {
friend class Box;
public:
~Thing() {/* delete thing */};
private:
Thing() {/* construct thing */};
Thing(const Thing& other) = delete;
Thing& operator=(Thing&& other) = delete;
};
class Box {
private:
std::vector<std::unique_ptr<Thing> > contents{};
public:
void makeThing() {
contents.push_back(nullptr);
contents.back().reset(new Thing()); // I can live with the 'new' keyword here.
}
};
我的问题是:通过推送nullptr
然后将其重置为新对象来添加到向量是否不好? (除了new
关键字,如果构造函数不抛出,这似乎没问题?
注意:我已经看到了克服私有构造函数和删除复制语义的替代方法。但是我上面写的东西有什么危险的问题吗?
注意 2:在这里明确说明它,因为它已经抓住了一些人:std::make_unique
不起作用,因为 Thing 构造函数是私有的。
我会采用这种结构:
auto thing = std::unique_ptr<Thing>(new Thing());
contents.push_back(std::move(thing));
因为这更描述了你真正想要做的事情。您的Box
对象创建一个Thing
对象,将所有权传递给unique_ptr
然后将该unique_ptr
移动到contents
。
并且不要使用contents.emplace_back(new Thing());
一个原因来确定它可能会泄漏。但是 - 恕我直言 - 更重要的部分是可读性和可维护性,如果你写contents.emplace_back(new Thing());
那么你需要检查contents.emplace_back
是否真的声称所有权(contents
真的是std::vector<std::unique_ptr<Thing> >
类型而不是std::vector<Thing *>
,std::move(thing)
和thing
是一个unique_ptr
很明显所有权被移动了。
您的解决方案的缺点是,在阅读contents.push_back(nullptr);
时不清楚为什么要添加nullptr
,您需要阅读下一行以了解原因。
我认为@t.niese有办法做到这一点。我想添加一个替代方案,稍微隐藏new
,并可能阻止任何人使用emplace_back(new Thing)
进行重构(可能会泄漏(。
#include <memory>
#include <vector>
class Thing {
friend class Box;
public:
~Thing() {/* delete thing */};
private:
Thing() {/* construct thing */};
Thing(const Thing& other) = delete;
Thing& operator=(Thing&& other) = delete;
static std::unique_ptr<Thing> allocate_thing() {
return std::unique_ptr<Thing>(new Thing);
}
};
class Box {
private:
std::vector<std::unique_ptr<Thing> > contents{};
public:
void makeThing() {
// DO NOT replace with emplace_back. Temporary prevents leekage in case of exception.
contents.push_back(Thing::allocate_thing());
}
};
我不喜欢这一点的是,人们可以想象Thing::allocate_thing()
做了一些奇怪的魔术(事实并非如此(,因此这使代码更加混乱。
我进一步想说,也许你应该考虑一下你的设计。虽然尝试减少对Thing
的访问是好的,但也许有更好的方法可以做到这一点(也许是匿名命名空间?当然,这不会阻止某人创建对象,如果他们可以在某个时候对向量的元素进行decltype
(。如果您更笼统地提出您的问题,而不仅仅是要求对这个解决方案发表评论,也许您可以得到一些很好的推荐。
通常你会使用make_unique
。但是由于Thing
的构造函数是私有的,Box
是朋友,所以你需要在Box
中构造新的Thing
,你不能使用std::make_unique
。但是你的方法是...奇怪。
如果您不能使用make_unique
则很容易产生泄漏的可能性。例如,要做的第一件事:
contents.emplace_back(new Thing()); // this can leak
将泄漏(如果矢量大小调整失败(
有一些解决方案可以解决这个问题,比如更长的解决方案:
contents.emplace_back(std::unique_ptr<Thing>(new Thing()));
或提前预订:
contents.reserve(contents.size() + 1); // this prevents the amortized constant complexity
// of `push_back`
// resulting in worst performance
contents.emplace_back(new Thing);
但问题仍然存在:编写泄漏代码很容易,正确的代码会做一些看起来不必要的额外事情,所以你需要注释和记录它们的目的,以便下一个程序员不会优化它们。
在我看来
make_unique
:class Thing {
friend std::unique_ptr<Thing> std::make_unique<Thing>();
// ...
};
class Box {
// ...
void makeThing() {
contents.emplace_back(std::make_unique<Thing>());
}
};
由于结交make_unique
朋友违背了私有构造函数的目的,因此我更喜欢 n314159 使用私有allocate_thing
方法的解决方案。
感谢下面的所有评论者帮助使这篇文章正确和更好。
我一直在玩这个,只是注意到我降落在@n31459最终到达的地方。唯一的补充是它使用完美转发到Thing
构造函数,因此如果您稍后添加更多构造函数,则只需要一个makeThing
函数和工厂函数Thing
。
#include <memory>
#include <utility>
#include <vector>
class Thing {
public:
~Thing() = default;
private:
explicit Thing(int Value) : value(Value) {}
Thing(const Thing&) = delete;
Thing(Thing&&) noexcept = delete;
Thing& operator=(const Thing&) = delete;
Thing& operator=(Thing&&) noexcept = delete;
int value; // some data
template<class... Args>
static std::unique_ptr<Thing> make_unique(Args&&... args) {
return std::unique_ptr<Thing>{new Thing(std::forward<Args>(args)...)};
}
friend class Box;
};
class Box {
public:
template<class... Args>
void makeThing(Args&&... args) {
contents.emplace_back(Thing::make_unique(std::forward<Args>(args)...));
}
private:
std::vector<std::unique_ptr<Thing>> contents{};
};
- 添加存储在向量中的大整数的函数出现问题
- 不允许在向量中添加更多元素
- 如何将元素添加到向量的字符串位置
- 有没有办法向这个向量添加元素?
- 这种方式是否可以接受向向量添加unique_ptr?
- C++:添加新结构时,结构指针向量中的所有元素都会更新
- C++ 将子类的对象添加到父类的向量中
- 如何在提升间进程中构建具有给定计数的向量并向其添加元素
- 将不同类型的变量添加到向量中
- 将树节点添加到向量向量中的 n 元树遍历的平均和最坏情况时间复杂度是多少?
- 使用 std::transform 将向量向量 (a) 添加到另一个向量向量 (b)
- 继承层次结构并将元素添加到向量
- 测试驱动开发 c++:如何将对象添加到向量中,将歌曲添加到播放列表并对其进行测试
- 如何将对象添加到类中的静态向量
- 对象如何将自身添加到对象类中的向量中?
- 为什么我们需要在优先级队列声明中添加一个向量作为参数?
- C++ 如何在向量中添加向量?vector.push_back( 向量<> )?
- 如何在CUDA内核中添加向量元素
- 向 NS2 添加向量;编译器错误
- 向向量添加向量