C++ 对在函数中创建的 Eigen::Matrix 的悬空引用
C++ dangling reference to Eigen::Matrix created in function
下面的例子会创建悬空引用吗? (因为 vector<int>
在函数中是局部的,而 MatrixXi
正在使用对此数据的引用)。还考虑到 Foo 构造函数引用了 MatrixXi
.
#include <Eigen/Dense>
#include <Eigen/Core>
using Eigen::MatrixXi;
static Eigen::MatrixXi CreateMatrix()
{
std::vector<int> data(4);
data = { 1, 2, 3, 4 };
Eigen::MatrixXi mat = Eigen::Map<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(&data[0], 2, 2);
return mat;
}
class Foo
{
public:
const MatrixXi matrix;
Foo(const MatrixXi &matrix) : matrix(matrix) {}
void Bar() {
std::cout << matrix << std::endl;
}
};
int main()
{
Foo test(CreateMatrix());
test.Bar();
Foo* test2 = new Foo(CreateMatrix());
test2->Bar();
std::cin.get();
delete test2;
}
编辑:
添加了 delete test2
以释放内存。
编辑2:
实际问题中 MatrixXi 的实际创建是基于函数的参数,看起来像这样 (C++/CLI):
static Eigen::MatrixXi CreateMatrix(MathNet::Numerics::LinearAlgebra::Matrix<float>^ data)
{
int length = data->RowCount * data->ColumnCount;
std::vector<int> data_out(length);
for (size_t i = 0; i < data->RowCount; i++)
{
for (size_t j = 0; j < data->ColumnCount; j++)
{
data_out[i * data->ColumnCount + j] = data[i, j];
}
}
Eigen::MatrixXi mat = Eigen::Map<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(&data_out[0], data->RowCount, data->ColumnCount);
return mat;
}
更新:
为了证明 davidhigh 的回答是正确的,我编写了以下测试:
int main()
{
std::vector<int> data = { 1, 2, 3, 4 };
auto map = Eigen::Map<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(&data[0], 2, 2);
Eigen::MatrixXi mat = map;
data[0] = 5;
std::cout << "mat:\n" << mat << std::endl;
std::cout << "map:\n" << map << std::endl;
std::cin.get();
}
控制台输出如下:
mat:
1 2
3 4
map:
5 2
3 4
所以没有悬空引用,因为 mat 不再连接到数据(而 map 是)。
您从 CreateMatrix() 调用返回的垫子保存着临时 std::vector 的地址,该地址超出了该函数的范围。
无法保证 address 仍会保留您想要的内容。
您需要使 std::vector 更永久。它必须存在于 CreateMatrix() 之外,否则您将遇到麻烦。
好的,由于这个线程的状态变得有点混乱,我将添加另一个答案并详细说明我已经在评论中写的内容。
return一个MatrixXi
函数里面CreateMatrix
和return就可以了。您通过从 Eigen::Map
复制来创建矩阵,这也可以。
在您的简单示例中,您可以通过直接初始化 MatrixXi
来绕过 Map
,例如通过 operator<<
。在更一般的情况下,也许你得到的只是一个向量,那么你的方法又可以了。
你 return 这里的 MatrixXi
很重要。如果您将 return Map
对象,这通常会导致悬空引用。然后,例如,如果您在外部调用 operator()
,则 Map
想要访问在 CreateMatrix()
退出时已经被破坏的向量。 (实际上,在您的特殊示例中,它会起作用,因为您直接使用结果创建 Foo
,它再次将 Map
存储在矩阵中——仍然,这不是一个好主意编写一个非常容易出错的函数)。
最后一点:不要用 test2
做你在主函数中做的原始指针。在这里完全没有用。使用 RAII,这基本上是您在 main 中的第一种方法。
下面的例子会创建悬空引用吗? (因为 vector<int>
在函数中是局部的,而 MatrixXi
正在使用对此数据的引用)。还考虑到 Foo 构造函数引用了 MatrixXi
.
#include <Eigen/Dense>
#include <Eigen/Core>
using Eigen::MatrixXi;
static Eigen::MatrixXi CreateMatrix()
{
std::vector<int> data(4);
data = { 1, 2, 3, 4 };
Eigen::MatrixXi mat = Eigen::Map<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(&data[0], 2, 2);
return mat;
}
class Foo
{
public:
const MatrixXi matrix;
Foo(const MatrixXi &matrix) : matrix(matrix) {}
void Bar() {
std::cout << matrix << std::endl;
}
};
int main()
{
Foo test(CreateMatrix());
test.Bar();
Foo* test2 = new Foo(CreateMatrix());
test2->Bar();
std::cin.get();
delete test2;
}
编辑:
添加了 delete test2
以释放内存。
编辑2:
实际问题中 MatrixXi 的实际创建是基于函数的参数,看起来像这样 (C++/CLI):
static Eigen::MatrixXi CreateMatrix(MathNet::Numerics::LinearAlgebra::Matrix<float>^ data)
{
int length = data->RowCount * data->ColumnCount;
std::vector<int> data_out(length);
for (size_t i = 0; i < data->RowCount; i++)
{
for (size_t j = 0; j < data->ColumnCount; j++)
{
data_out[i * data->ColumnCount + j] = data[i, j];
}
}
Eigen::MatrixXi mat = Eigen::Map<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(&data_out[0], data->RowCount, data->ColumnCount);
return mat;
}
更新:
为了证明 davidhigh 的回答是正确的,我编写了以下测试:
int main()
{
std::vector<int> data = { 1, 2, 3, 4 };
auto map = Eigen::Map<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(&data[0], 2, 2);
Eigen::MatrixXi mat = map;
data[0] = 5;
std::cout << "mat:\n" << mat << std::endl;
std::cout << "map:\n" << map << std::endl;
std::cin.get();
}
控制台输出如下:
mat:
1 2
3 4
map:
5 2
3 4
所以没有悬空引用,因为 mat 不再连接到数据(而 map 是)。
您从 CreateMatrix() 调用返回的垫子保存着临时 std::vector 的地址,该地址超出了该函数的范围。 无法保证 address 仍会保留您想要的内容。 您需要使 std::vector 更永久。它必须存在于 CreateMatrix() 之外,否则您将遇到麻烦。
好的,由于这个线程的状态变得有点混乱,我将添加另一个答案并详细说明我已经在评论中写的内容。
return一个
MatrixXi
函数里面CreateMatrix
和return就可以了。您通过从Eigen::Map
复制来创建矩阵,这也可以。在您的简单示例中,您可以通过直接初始化
MatrixXi
来绕过Map
,例如通过operator<<
。在更一般的情况下,也许你得到的只是一个向量,那么你的方法又可以了。你 return 这里的
MatrixXi
很重要。如果您将 returnMap
对象,这通常会导致悬空引用。然后,例如,如果您在外部调用operator()
,则Map
想要访问在CreateMatrix()
退出时已经被破坏的向量。 (实际上,在您的特殊示例中,它会起作用,因为您直接使用结果创建Foo
,它再次将Map
存储在矩阵中——仍然,这不是一个好主意编写一个非常容易出错的函数)。最后一点:不要用
test2
做你在主函数中做的原始指针。在这里完全没有用。使用 RAII,这基本上是您在 main 中的第一种方法。