如何将 shared_ptr 变量推回 C++ 中的 shared_ptr 向量?

Posted

技术标签:

【中文标题】如何将 shared_ptr 变量推回 C++ 中的 shared_ptr 向量?【英文标题】:How do you push_back a shared_ptr variable to a vector of shared_ptrs in C++? 【发布时间】:2021-11-09 02:06:43 【问题描述】:

我是一名 C++ 初学者,具有 Python、Java 和 JS 方面的背景,所以我仍在学习指针方面的技巧。

我有一个共享指针向量。在另一个函数内部,我将一个共享指针分配给一个变量并将其添加到向量中。如果我在该函数退出后尝试访问添加的元素,则会发生分段错误:

class Bar

private:
    std::vector<std::shared_ptr<Foo>> fooVector;


void Bar::addToFoo()

    std::shared_ptr<Foo> foo (new Foo(…));
    fooVector.push_back(foo);

void Bar::otherMethod()

    // this method gets called sometime after addToFoo gets called
    …
    fooVector[anIndex]->baz(); // segfaults
    …

但是,如果 push_back 是共享指针而不是变量,它可以工作。

// this works:
fooVector.push_back(std::shared_ptr<Foo>(new Foo(…)));

// this segfaults:
std::shared_ptr<Foo> foo (new Foo(…));
fooVector.push_back(foo);

我相信这是因为foo 变量在addToFoo 函数退出时被删除(如果我错了,请纠正我)。你如何在 C++ 中将 push_back shared_ptr 变量 转换为 vectorshared_ptrs


为什么要使用变量

虽然直接将shared_ptrs 推送到不带变量的向量是可行的,但我更喜欢使用变量来执行此操作:

std::shared_ptr<Rider> rider;
switch (iProcessorModesParam)

    case PEAKS_MODE:
        rider = std::shared_ptr<Rider>(new PeaksRider(…));
        break;
    case RMS_MODE:
        rider = std::shared_ptr<Rider>(new RMSrider(…));
        break;

volumeRiders.push_back(rider);

PeaksRider 和 RMSrider 是 Rider 的子类。我想将 Rider 的所有子类型存储在同一个 Rider 向量中。我了解到将 Rider 的子类型添加到 Rider 的向量中不起作用,并且需要指针来实现这种多态性:

std::vector<Rider> // doesn’t work with subtypes

std::vector<*Rider>
std::vector<std::shared_ptr<Rider>>

拥有std::shared_ptr&lt;Rider&gt; rider; 变量可以避免为每种类型的Rider 重复.push_back(…) 代码。

【问题讨论】:

fooVector.push_back(foo); 是正确的。如果您的程序崩溃,那么很可能是由于程序中其他地方的错误。请发帖***.com/help/minimal-reproducible-example 显示的代码甚至不应该编译,因为fooVector[0].baz(); 试图以shared_ptr 本身的成员而不是Foo 对象的成员身份访问baz()。您需要改用fooVector[0]-&gt;baz();。无论哪种方式,在访问第一个元素之前确保vector 不为空,因为使用向量的operator[] 越界索引是未定义的行为 虽然这不太可能是您的问题的原因,但创建共享指针的首选方法是使用std::make_shared() 相关 - ***.com/questions/51133106 你能发一个minimal reproducible example吗? 【参考方案1】:

不是分配共享指针,而是用户reset 方法。

rider.reset(new PeaksRider(…));

除此之外,您的代码 sn-ps 对我来说似乎没问题。

segfault 可能是由于索引变量(可能超出范围)引起的。我建议你使用.at(index) 来访问vector 中的指针并将这部分代码包装在try..catch 块中,看看真正的错误是什么。

关于……

我相信这是因为 foo 变量在 addToFoo 函数退出时被删除(如果我错了,请纠正我)。

这不是真的,share_ptrs 使用本地计数器来进行#of 引用。一旦您将指针推送到向量,计数器就会递增到 2,并且在控制退出函数后,计数器会递减到 1。因此,您的对象还没有被销毁。

【讨论】:

【参考方案2】:

创建共享指针实例,将其存储在变量中,然后对向量执行 push_back 没有问题。只要您在调用“otherMethod”时使用的索引有效,您的代码就应该没问题。不过,我对您的代码有几点建议:

当您创建 shared_ptr 时,强烈建议您通过“std::make_shared”来创建,以确保您的代码在所有情况下的安全性和正确性。在另一篇文章中,您会找到一个很好的解释:Difference in make_shared and normal shared_ptr in C++ 使用可能包含会导致越界访问(通常会导致分段错误)的值的变量访问向量的位置时,最好在使用向量之前放置断言,因此您将检测这些不良情况。

我刚刚写了一个小sn-p,你可以测试一下来说明我刚才提到的内容:

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

class Foo

public:
    int data = 0;
;

class Bar

public:
    void addNewFoo(int d)
    
        std::shared_ptr<Foo> foo(new Foo());
        foo->data = d;
        fooVector.push_back(foo);
    

    void addNewFooImproved(int d)
    
        auto foo = std::make_shared<Foo>();
        foo->data = d;
        fooVector.push_back(foo);
    

    void printFoo(int idx)
    
        assert(idx < fooVector.size());
        std::cout << fooVector[idx]->data << std::endl;
    

private:
    std::vector<std::shared_ptr<Foo>> fooVector;
;

int main()

    Bar b;
    b.addNewFoo(10);
    b.addNewFoo(12);
    b.addNewFooImproved(22);
    b.printFoo(1);
    b.printFoo(2);
    b.printFoo(0);

【讨论】:

以上是关于如何将 shared_ptr 变量推回 C++ 中的 shared_ptr 向量?的主要内容,如果未能解决你的问题,请参考以下文章

将特征 C++ 块表达式推回 std::vector

使用 boost::shared_ptr 访问静态成员变量

[C++]智能指针unique_ptr,shared_ptr,weak_ptr

将指针向量元素推回非指针向量c ++时出错

C++:shared_ptr

C++中的shared_ptr和weak_ptr