我想从标准容器中使用许多与继承相关的类型(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
容器的正确方法是什么?
答案 0 :(得分:2)
您不能引用函数本地对象。函数退出后,这些本地对象将被销毁,向量中的悬挂引用将留下您。解决此问题的方法是切换到使用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;
}
答案 1 :(得分:0)
您面临的问题是S1 x
函数的结尾处的对象S2 y
和init
被破坏了。因此,在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;
}
这也会产生segmentation fault
,因为get_i()
返回对局部变量的引用(如果get_i()
结束,则该变量将被销毁)。
您可以使用std::unique_ptr
代替注释之一中提到的内容。
答案 2 :(得分:0)
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
在容器不想拥有对象的情况下很有用。