如何将指针语义添加到C++ STL中?

问题描述 投票:0回答:1

我最近正在研究C++ STL,我将讨论一个使用

deque
的例子。请注意,
deque
只是一个示例,我们还可以使用
vector
或其他。

背景

最小的可重现示例是一个简单的多队列生产者-消费者问题。考虑一个可以自己洗衣服的智能洗衣机,以及一个可以从洗衣机中取出衣服并烘干的智能烘干机。洗衣机可以被认为是多件未洗衣服的队列,而烘干机可以被认为是已洗衣服的队列。

class Clothes
为衣服单位。生产者将不断生成
Clothes
并将它们添加到洗衣机队列中。当洗衣机完成一件
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
,因为它是由复制构造函数构造的。现在,
Clothes
的数量仍然是一个。

// 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
的析构函数。我尝试通过
Clothes
访问
c.val
对象中的一些字段(假设
val
Clothes
中的字段),我发现我仍然可以访问它,这意味着
Clothes c
没有被破坏。这就是为什么我很困惑。为什么调用析构函数,哪个
Clothes
对象被析构?

我的想法

我认为情况1可能是正确的方法,但情况2似乎也是正确的,尽管发生了一些意想不到的事情。

c++ vector stl deque
1个回答
0
投票

代码中的问题:

您是正确的,在情况 1 中创建了

Cloths
的副本。 您也是正确的,在情况 2
c
中没有复制。
但是,在情况 2 中,
c
是对已破坏对象的悬空引用,访问它会导致 未定义的行为。这意味着任何事情都可能发生,包括“工作”代码的出现。

解决方案:

您可以使用

std::shared_ptr<Clothes>
作为容器中的元素。这将避免复制
Clothes
对象并产生悬空引用。
std::shared_ptr
自动管理对象(在本例中为
Clothes
类型)的生命周期。当不再需要时它将被释放(这是通过为每个对象保留引用计数来完成的)。
请注意,创建
shared_ptr
的推荐方法是通过
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
使用起来更直接。

© www.soinside.com 2019 - 2024. All rights reserved.