0

我有许多我想从标准容器中使用的继承相关类型(std::reference_wrapper对于这种容器来说,AFAIU 是一个合适的值类型)。但是,我不明白,当插入到映射中的引用值不是全局变量时,如何初始化这样的容器。例如:

#include <iostream>
#include <vector>
#include <functional>

using namespace std;

struct I
{
    virtual void print() = 0;
};

struct S1: public I
{
    void print() override
    {
        cout << "S1 " << i << endl;
    }
    int i{};
};

struct S2: public I
{
    void print() override
    {
        cout << "S2 " << f << endl;
    }
    float f{};
};

std::vector<reference_wrapper<I>> v;

void init()
{
    S1 x{};
    S2 y{};
    v.emplace_back(x);
    v.emplace_back(y);
}

int main()
{
    init();
    v[1].get().print();
    return 0;
}

这可以编译,但我在运行时会出现一些内存损坏。初始化容器的正确方法是std::reference_wrapper什么?

4

2 回答 2

2

您不能引用函数本地对象。一旦函数退出,这些本地对象就会被销毁,并且向量中会留下悬空引用。解决此问题的方法是切换到使用 astd::unique_ptr<I>std::make_unique动态分配要存储在向量中的对象。将std::unique_ptr管理内存,一旦向量被销毁,它将销毁向量中的std::unique_ptr's,它们将依次删除为保存对象而获得的内存。那会给你

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

using namespace std;

struct I
{
    virtual void print() = 0;
};

struct S1: public I
{
    void print() override
    {
        cout << "S1 " << i << endl;
    }
    int i{};
};

struct S2: public I
{
    void print() override
    {
        cout << "S2 " << f << endl;
    }
    float f{};
};

std::vector<unique_ptr<I>> v;

void init()
{
    v.emplace_back(std::make_unique<S1>()); // creates a defaulted S1 in the unique_ptr
    v.emplace_back(std::make_unique<S2>()); // creates a defaulted S2 in the unique_ptr
}

int main()
{
    init();
    v[1]->print(); // or (*v[1]).print()
    return 0;
}
于 2019-09-09T13:15:04.160 回答
0

您面临的问题是您的对象在函数结束时被销毁S1 x。因此,在 结束时,您的向量包含对任何内容的引用。因此,当您尝试调用 时,您会得到一个.S2 yinitinit()vprint()segmentation fault

以类似的方式,考虑以下代码:

int& get_i()
{
    int i = 1;
    return i;
}

int main()
{
    std::cout << get_i() << std::endl; // segmentation fault
    return 0;
}

这也产生了一个segmentation faultasget_i()返回一个对局部变量的引用(它在最后被销毁 if get_i())。

您可以std::unique_ptr改用其中一条评论中提到的用户。

于 2019-09-09T13:51:16.897 回答