我最近正在研究 C++ STL,我将讨论一个使用 deque
的示例。请注意,deque
只是一个示例,我们还可以使用 vector
或其他。
背景
最小的可重现示例是一个简单的多队列生产者-消费者问题。考虑一个可以自己洗衣服的智能洗衣机,以及一个可以从洗衣机中取出衣服并烘干的智能烘干机。洗衣机可以被认为是多件未洗衣服的队列,而烘干机可以被认为是已洗衣服的队列。
让Clothes
类成为衣服的单位。生产者将不断生成衣服
并将它们添加到洗衣机队列中。当洗衣机洗完一件衣服
后,它会将其取出并添加到烘干机队列中。
问题
如果我们要在 C 语言中执行此操作,则使用指针 (malloc()
) 和链表会很简单。通过移动指针,我们可以保证不会分配重复的Clothes
。完成后,我们可以简单地调用 free()
。
我们可以在 C++ STL 中做同样的事情吗?实现这一目标的推荐方法是什么?
我尝试过什么以及什么让我感到困惑
我进行了一些测试,发现 C++ STL 自己处理内存分配和释放,这让我很困惑。
例如,(假设我们在本例中使用deque
):
std::deque<Clothes> washer = {...}; // only 1 object
std::deque<Clothes> dryer; // empty
// case 1: copy constructor is invoked, 2 memory locations occupied
Clothes c = washer.front();
washer.pop_front(); // destructor is invoked, I think this corresponds to the original Clothes
上面调用了复制构造函数并产生 1 个重复的拷贝。操作 pop_front()
隐式调用 Clothes
对象的析构函数。但它不会影响c
,因为它是由复制构造函数构造的。现在,衣服
的数量仍然是一件。
// case 2: copy constructor is not invoked
Clothes &c = washer.front();
washer.pop_front(); // destructor is invoked again, but there is only one Clothes object
上面的代码通过引用获取了washer
中的第一个对象,并且没有调用复制构造函数。我假设现在仍然只有一个 Clothes
对象。但是,当我们调用 pop_front()
时,会调用 Clothes
的析构函数。我尝试通过 c.val
访问 Clothes
对象中的某些字段(假设 val
是 Clothes
中的一个字段) ,我发现我仍然可以访问它,这意味着 Clothes c
没有被破坏。这就是为什么我很困惑。为什么调用析构函数,以及哪个 Clothes
对象被析构?
我的想法
我认为情况 1 可能是正确的方法,但情况 2 似乎也是正确的,尽管发生了一些意想不到的事情。
最佳答案
代码中的问题:
您是正确的,在情况 1 中,Cloths
的拷贝被 build 。
你也是正确的,在情况 2 c
中没有复制。
然而,在情况2 c
是对已破坏对象的悬空引用,访问它会导致 undefined behavior 。这意味着任何事情都可能发生,包括“工作”代码的出现。
解决方案:
C++ 提供智能指针。
您可以使用 std::shared_ptr<Clothes>
作为容器中的元素。这将避免同时复制 Clothes
对象并具有悬空引用。
一个std::shared_ptr
自动管理对象(在本例中为 Clothes
类型)的生命周期。当不再需要时它将被释放(这是通过为每个对象保留引用计数来完成的)。
注意推荐的创建方式shared_ptr
s 是通过 std::make_shared
.
#include <deque>
#include <memory>
class Clothes
{
// ...
};
int main()
{
std::deque<std::shared_ptr<Clothes>> washer = { std::make_shared<Clothes>() };
std::deque<std::shared_ptr<Clothes>> dryer;
auto c = washer.front();
washer.pop_front();
dryer.push_back(c);
}
最后请注意,还有 std::unique_ptr
,它支持单一所有者且无复制(仅移动)。然而在这种情况下,在我看来std::shared_ptr
使用起来更直接。如果你想使用std::unique_ptr
,它应该是这样的:
#include <deque>
#include <memory>
class Clothes
{
// ...
};
int main()
{
std::deque<std::unique_ptr<Clothes>> washer;
washer.push_back(std::make_unique<Clothes>());
std::deque<std::unique_ptr<Clothes>> dryer;
auto c = std::move(washer.front());
washer.pop_front();
dryer.push_back(std::move(c));
}
关于c++ - 如何将指针语义添加到 C++ STL 中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76809733/