C++11存储多个共享指针作为原始指针

16

我的问题涉及C++11中的shared_ptrmake_shared。我有两个向量,第一个存储智能指针,第二个存储原始指针。第一个向量的工作方式符合我的预期,但是第二个向量令人困惑...

代码示例

#include <iostream>
#include <vector>
#include <memory>

int main() {
    std::vector<std::shared_ptr<int>> vector1;
    vector1.push_back(std::make_shared<int>(1));
    vector1.push_back(std::make_shared<int>(2));
    vector1.push_back(std::make_shared<int>(3));

    std::vector<int*> vector2;
    vector2.push_back(std::make_shared<int>(4).get());
    vector2.push_back(std::make_shared<int>(5).get());
    vector2.push_back(std::make_shared<int>(6).get());

    std::cout << "vector1 values:" << std::endl;
    for(auto &value: vector1) { std::cout << *value << std::endl; }

    std::cout << "vector2 values:" << std::endl;
    for(auto &value: vector2) { std::cout << *value << std::endl; }

    return 0;
}

输出

vector1 values:
1
2
3
vector2 values:
6
6
6


问题

我意识到一开始使用原始指针而不是尝试转换智能指针会简单得多,但这让我很好奇为什么会发生这种情况?此外,为什么每次push操作都会更改vector2中的所有值?


链接

这里有一些我在stackoverflow上找到的问题,但它们没有回答我的问题,或者也许我没有理解答案...


2
很棒的第一个问题!格式非常好。 - yizzlez
2个回答

10

您正在看到未定义行为。 当您执行以下操作时:

vector2.push_back(std::make_shared<int>(4).get());

你正在创建一个临时的shared_ptr,并将其管理对象的指针复制到你的vector中。这会立刻变成一个悬空指针。


10
你使用 shared_ptr 的原因是希望当所有指向它的实例超出范围时,它所指向的内存会被释放。当你调用 .get() 方法后,shared_ptr 会立即被销毁,因此你会立即得到一个悬空指针。对这个指针进行解引用操作的结果是未定义的,这意味着它可能返回一个有意义的值,也可能不返回或者执行完全无关的操作(比如崩溃)。
这是一种特性。你希望这种情况发生,否则你会造成内存泄漏。想象一下下面的代码:
vector<int> integers;
integers.push_back(*make_shared<int>(6).get());
如果未释放内存,则以后将无法释放它,因为您无法恢复shared_ptr的托管指针。

1
现在这有点更有意义了,但是如果std::shared_ptr::get返回一个全新的指针而不是指向管理对象的指针,那不是更有意义吗? - user2442944
2
@Oak,这将强制使用shared_ptr的任何类型都可以被复制,并且由于C++的复制机制,它很可能无法与多态一起使用。对于大型数据结构,它也会非常慢,并且还会给开发人员带来释放指针的负担(这违背了首次使用shared_ptr的初衷)。您也不能将其用于数组类型,因为无法获取使用new创建的数组的长度(因此shared_ptr在返回新指针时不知道要复制多少数据)。 - zneak
啊,非常感谢您提供详细易懂的答案 :) - user2442944
1
@Oak 不行,因为get()方法用于从智能指针中提取指针,而不是获取全新的指针。你可能会得到一个'6'作为输出,因为你的三个分配可能都得到了同一块内存,因此你只得到了最后一个值。然而(正如其他地方提到的),这是未定义的行为,因为你正在引用已经被删除的指针。 - Andre Kostur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接