C++ 这种方式是否可用于向向量添加唯一的_ptr?
假设我有一个盒子。它包含由C++ 这种方式是否可用于向向量添加唯一的_ptr?,c++,C++,假设我有一个盒子。它包含由框生成的内容,并且仅包含框。这些东西也是不可复制的 还有一种所有权关系,所以我想使用unique\u ptrs。(如果盒子坏了,一切都会随之发生。) #包括 #包括 阶级事务{ 朋友类盒子; 公众: ~Thing(){/*删除Thing*/}; 私人: Thing(){/*构造Thing*/}; 事物(常量事物和其他)=删除; 对象和运算符=(对象和其他)=删除; }; 类框{ 私人: std::向量内容{}; 公众: void makeThing(){ 内容。推回(n
框
生成的内容,并且仅包含框
。这些东西也是不可复制的
还有一种所有权关系,所以我想使用unique\u ptr
s。(如果盒子坏了,一切都会随之发生。)
#包括
#包括
阶级事务{
朋友类盒子;
公众:
~Thing(){/*删除Thing*/};
私人:
Thing(){/*构造Thing*/};
事物(常量事物和其他)=删除;
对象和运算符=(对象和其他)=删除;
};
类框{
私人:
std::向量内容{};
公众:
void makeThing(){
内容。推回(nullptr);
contents.back().reset(newthing());//我可以在这里使用'new'关键字。
}
};
我的问题是:通过推一个nullptr
然后将其重置为一个新对象来添加到向量是否不好?(除了new
关键字之外,如果构造函数不抛出该关键字,这似乎很好?)
注意:我已经看到了克服私有构造函数和删除复制语义的方法。但是我上面写的有什么危险的错误吗?
注2:在这里明确说明,因为它已经吸引了一些人:
std::make_unique
将不起作用,因为构造函数是私有的。通常您会使用make_unique
。但是由于Thing
的构造函数是私有的,Box
是朋友,因此需要在Box
中构造新的Thing
,不能使用std::make_unique
。但是你的方法是。。。奇怪
如果不能使用使_唯一
很容易产生泄漏的可能性。例如,第一件显而易见的事情是:
contents.emplace_back(new Thing()); // this can leak
将泄漏(如果向量大小调整失败)
有一些解决方案可以解决这个问题,比如更长的时间:
contents.emplace_back(std::unique_ptr<Thing>(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>());
}
};
类的东西{
friend std::unique_ptr std::make_unique();
// ...
};
类框{
// ...
void makeThing(){
contents.emplace_back(std::make_unique());
}
};
由于使make_unique
friend违背了私有构造函数的目的,我更喜欢使用私有allocate_thing
方法
感谢下面所有的评论者帮助这篇文章变得正确和更好。我将采用以下结构:
auto thing = std::unique_ptr<Thing>(new Thing());
contents.push_back(std::move(thing));
auto thing=std::unique_ptr(new thing());
内容。推回(std::move(thing));
因为这更多地描述了你真正想要做的事情。您的框
对象创建一个对象
对象,将所有权传递给唯一\u ptr
,然后您将该唯一\u ptr
移动到内容
不要使用内容代码>确定它可能泄漏的一个原因。但是-imho-更重要的部分是可读性和可维护性,如果您编写内容代码>然后你需要检查内容。emplace\u back
是否真的声称所有权(即内容
确实属于std::vector
类型,而不是std::vector
,其中std::move(thing)
和thing
是一个唯一的ptr
很明显所有权被移动了
您的解决方案的缺点是,在阅读内容时,向后推(nullptr)
添加nullptr
的原因尚不清楚,您需要阅读下一行来了解原因。我认为@t.niese有办法做到这一点。我想添加一个替代方案,隐藏新的并可能防止任何人使用emplace_back(新事物)
进行重构(这可能会泄漏)
#包括
#包括
阶级事务{
朋友类盒子;
公众:
~Thing(){/*删除Thing*/};
私人:
Thing(){/*构造Thing*/};
事物(常量事物和其他)=删除;
对象和运算符=(对象和其他)=删除;
静态std::unique_ptr allocate_thing(){
return std::unique_ptr(新事物);
}
};
类框{
私人:
std::向量内容{};
公众:
void makeThing(){
//不可替换为emplace_back。在异常情况下,临时可防止下陷。
contents.push_back(Thing::allocate_Thing());
}
};
我不喜欢的是,可以想象,Thing::allocate\u Thing()
会产生一些奇怪的魔力(事实并非如此),因此这会使代码更加混乱
我还想说,也许你应该考虑一下你的设计。虽然尝试减少对东西的访问是好的,但也许有更好的方法可以做到这一点(可能是匿名名称空间?当然,如果某人可以在某个点对向量的元素执行decltype
,这不会阻止他们创建对象)。如果你能更一般地提出你的问题,而不是仅仅征求对这一解决方案的意见,也许你能得到一些好的建议。我一直在玩这个,刚刚注意到我大概在@n31459结束的地方着陆。唯一的补充是,它使用到东西的完美转发o如果以后添加更多构造函数,则只需要一个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{};
};
#包括
#包括
#包括
阶级事务{
公众:
~Thing()=默认值;
私人:
显式事物(int值):值(Value){}
事物(const Thing&)=删除;
事物(事物&)
auto thing = std::unique_ptr<Thing>(new Thing());
contents.push_back(std::move(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{};
};