为什么我的对象在我将它们的 unique_ptr 添加到向量后会被解构两次?
Why do my objects get deconstructed twice after I add their unique_ptr to a vector?
我正在尝试为 Example
class 创建一个 unique_ptr
的向量,并且出于某种原因,每次我调用 emplace_back()
时,对象都会被解构两次。我认为如果我只在这里显示代码,您会更容易理解:
#include <vector>
#include <iostream>
#include <memory>
class Example;
typedef std::unique_ptr<Example> ExampleUPointer;
std::vector<ExampleUPointer> ExampleList;
class Example {
short index;
public:
Example()
: index (ExampleList.size())
{
ExampleList.emplace_back(std::make_unique<Example>(*this));
printf("Example %d was constructed\n", index);
}
~Example() {
printf("Example %d was deconstructed\n", index);
}
};
然后我继续在 main()
中创建 Example
class 的五个对象。这是输出:
Example 0 was constructed
Example 1 was constructed
Example 2 was constructed
Example 3 was constructed
Example 4 was constructed
Example 4 was deconstructed
Example 3 was deconstructed
Example 2 was deconstructed
Example 1 was deconstructed
Example 0 was deconstructed
Example 0 was deconstructed
Example 1 was deconstructed
Example 2 was deconstructed
Example 3 was deconstructed
Example 4 was deconstructed
为什么会这样?
ExampleList.emplace_back(std::make_unique<Example>(*this));
如果你用纸和铅笔解决这里发生的事情,你会发现 Example
的第二个实例就在这里复制构造。
但是您的 class 有一个默认的复制构造函数,当然它不会记录任何内容。
所以,您真正看到的是原始对象和副本的析构函数都被调用了。没有记录复制构造。
添加显式复制构造函数后,您将看到正在发生的事情的全貌,该复制构造函数记录对象的哪个实例被复制构造。
I add their unique_ptr to a vector?
不,你没有那样做。您在动态范围内使用 make_unique
创建每个对象的副本(因为它就是这样做的),并将新构建的副本的 unique_ptr
添加到向量中。
A std::unique_ptr
拥有 它指向的内容。因此,对于对象的构造函数(它已经有一个所有者 - 无论正在构造对象)能够创建指向自身的有效唯一指针没有任何意义。当然,这不是你正在做的。正如 Sam 的回答所说,您正在创建一个全新的 Example
对象,一个副本,并将其添加到向量中。
一个选项是让向量不拥有对象。只需在向量中存储一个原始指针
std::vector<Example*> ExampleList;
...
Example()
{
ExampleList.emplace_back(this);
}
这将要求您确保 Example
对象至少与向量中指向它的指针一样长。您可以让析构函数自动将其从向量中删除。
另一种选择是使用一个工厂函数来创建对象,将其添加到向量(然后向量将拥有该对象),以及 return 指向它的指针。对象的生命周期现在由向量维护:
class Example;
std::vector<std::unique_ptr<Example>> ExampleList;
class Example {
public:
static Example *create()
{
// Create a new Example object and add it to the vector
// The unique_ptr in the vector has ownership of the object
ExampleList.push_back(std::make_unique<Example>());
// Return a pointer to the object
return ExampleList.back().get();
}
private:
// Can't call directly
Example() { ... }
};
每次使用 int 构造函数创建对象时,该构造函数都会通过调用复制构造函数创建另一个完全不同的对象。
Example()
: index (ExampleList.size())
{
ExampleList.emplace_back(std::make_unique<Example>(*this)); //<--- here
printf("Example %d was constructed\n", index);
就在那里。你做了第二个对象。
所以你用 int 构造函数构造了 5 个对象,用复制构造函数构造了 5 个对象,然后销毁了 10 个对象。
复制的对象与int构造的对象具有相同的索引。这就是复制的意思。
我正在尝试为 Example
class 创建一个 unique_ptr
的向量,并且出于某种原因,每次我调用 emplace_back()
时,对象都会被解构两次。我认为如果我只在这里显示代码,您会更容易理解:
#include <vector>
#include <iostream>
#include <memory>
class Example;
typedef std::unique_ptr<Example> ExampleUPointer;
std::vector<ExampleUPointer> ExampleList;
class Example {
short index;
public:
Example()
: index (ExampleList.size())
{
ExampleList.emplace_back(std::make_unique<Example>(*this));
printf("Example %d was constructed\n", index);
}
~Example() {
printf("Example %d was deconstructed\n", index);
}
};
然后我继续在 main()
中创建 Example
class 的五个对象。这是输出:
Example 0 was constructed
Example 1 was constructed
Example 2 was constructed
Example 3 was constructed
Example 4 was constructed
Example 4 was deconstructed
Example 3 was deconstructed
Example 2 was deconstructed
Example 1 was deconstructed
Example 0 was deconstructed
Example 0 was deconstructed
Example 1 was deconstructed
Example 2 was deconstructed
Example 3 was deconstructed
Example 4 was deconstructed
为什么会这样?
ExampleList.emplace_back(std::make_unique<Example>(*this));
如果你用纸和铅笔解决这里发生的事情,你会发现 Example
的第二个实例就在这里复制构造。
但是您的 class 有一个默认的复制构造函数,当然它不会记录任何内容。
所以,您真正看到的是原始对象和副本的析构函数都被调用了。没有记录复制构造。
添加显式复制构造函数后,您将看到正在发生的事情的全貌,该复制构造函数记录对象的哪个实例被复制构造。
I add their unique_ptr to a vector?
不,你没有那样做。您在动态范围内使用 make_unique
创建每个对象的副本(因为它就是这样做的),并将新构建的副本的 unique_ptr
添加到向量中。
A std::unique_ptr
拥有 它指向的内容。因此,对于对象的构造函数(它已经有一个所有者 - 无论正在构造对象)能够创建指向自身的有效唯一指针没有任何意义。当然,这不是你正在做的。正如 Sam 的回答所说,您正在创建一个全新的 Example
对象,一个副本,并将其添加到向量中。
一个选项是让向量不拥有对象。只需在向量中存储一个原始指针
std::vector<Example*> ExampleList;
...
Example()
{
ExampleList.emplace_back(this);
}
这将要求您确保 Example
对象至少与向量中指向它的指针一样长。您可以让析构函数自动将其从向量中删除。
另一种选择是使用一个工厂函数来创建对象,将其添加到向量(然后向量将拥有该对象),以及 return 指向它的指针。对象的生命周期现在由向量维护:
class Example;
std::vector<std::unique_ptr<Example>> ExampleList;
class Example {
public:
static Example *create()
{
// Create a new Example object and add it to the vector
// The unique_ptr in the vector has ownership of the object
ExampleList.push_back(std::make_unique<Example>());
// Return a pointer to the object
return ExampleList.back().get();
}
private:
// Can't call directly
Example() { ... }
};
每次使用 int 构造函数创建对象时,该构造函数都会通过调用复制构造函数创建另一个完全不同的对象。
Example()
: index (ExampleList.size())
{
ExampleList.emplace_back(std::make_unique<Example>(*this)); //<--- here
printf("Example %d was constructed\n", index);
就在那里。你做了第二个对象。
所以你用 int 构造函数构造了 5 个对象,用复制构造函数构造了 5 个对象,然后销毁了 10 个对象。
复制的对象与int构造的对象具有相同的索引。这就是复制的意思。