繁体   English   中英

在本地使用reference_wrapper初始化向量

[英]Initializing vector with reference_wrapper's locally

我想从标准容器中使用许多与继承相关的类型( 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的正确方法是什么?

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

#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;
}

您面临的问题是init函数结束时销毁了对象S1 xS2 y 因此,在init()的末尾,向量v包含对任何内容的引用。 因此,当尝试调用print() ,您会遇到segmentation fault

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

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

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

由于get_i()返回对局部变量的引用(如果get_i()最终将其销毁),这也会产生segmentation fault

您可以使用std::unique_ptr代替注释之一中提到的那样。

std::reference_wrapper旨在将引用存储在可复制和可分配的对象中,这些对象可存储在诸如std::vector类的容器中。 但是reference_wrapper不拥有所引用的对象,因此必须由其他对象来拥有。

被引用对象的生存期应比reference_wrapper的生存期更长,因此OP代码中的问题是init()将函数本地对象的引用置于v init()返回时,这些对象的生命周期结束。 可以避免:

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

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

struct S1: public I
{
    S1(int in) 
        : I(), i(in)
    { }

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

struct S2: public I
{
    S2(float fn) 
        : I(), f(fn)
    { }

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

// x owns couple S1 objects
S1 x[] = {1,3,5,7}; 
// y owns couple S2 objects
S2 y[] = {2,4,6,8};
// v can refer to S1 and S2 objects owned by others
std::vector<std::reference_wrapper<I>> v; 

// makes v to refer all objects in x and y
void init()
{
    copy(&x[0], &x[sizeof x / sizeof x[0]], back_inserter(v));
    copy(&y[0], &y[sizeof y / sizeof y[0]], back_inserter(v));
}

int main()
{
    init();
    for (auto e : v)
    {
        e.get().print();
    }
    return 0;
}

std::unique_ptr向量(建议其他答案)在容器要拥有对象时有用,而在容器不打算拥有对象时std::reference_wrapper有用。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM