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{};
};