在函数超出范围后保留特征矩阵不被删除

Preserve Eigen matrix from deleting after the function goes out of the scope

我的 Eigen::MatrixXf m 存储了一些数据,我需要在另一个数组 vtkFloatArray 中使用这些数据。为了避免复制我做的数据:

vtkNew<vtkFloatArray> array;    // `vtkNew` is a smart pointer class
{
    Eigen::MatrixXf m(2, 2);
    m << 1, 2, 3, 4;
    array->SetArray(m.data(), m.size(), 1);
}
// `array` must go on living and have access to the `.data()` pointer

问题是 array 必须比本征矩阵 m 长得多,但是当执行超出范围时 array 似乎无法获取值因为它们被移除(特征矩阵 m 在超出范围时被删除)。

看来我可以动态分配特征矩阵:

vtkNew<vtkFloatArray> array;
{
    Eigen::MatrixXf* m = new Eigen::MatrixXf(2, 2);
    m << 1, 2, 3, 4;
    array->SetArray(m->data(), m->size(), 1);
}
// `array` must go on living and have access to the `.data()` pointer

然而 SetArray() 有一些选项可以保存和删除数组 我怀疑如果我使用这种方法我会发生内存泄漏。可能 std::shared_ptr<Eigen::MatrixXf> m 可以帮助我,但我不确定它应该计算矩阵 m 上的引用和 vtkFloatArraym.data() 指针的引用(尽管我可能弄错了)和我也会内存泄漏。

如果有人知道如何在 vtkFloatArray 存活时保持我的特征矩阵存活并且不会发生内存泄漏,请解释一下。

此致

我的解决方案是基于@Peter-ReinstateMonica 的建议:创建一个包装器 class,对象 vtkFloatArrayEigen::MatrixXf 都将驻留其中,这样可以控制对象的生命周期这两个对象。

让我们调用包装器 class vtkEigenFloatArray 并让它继承自 vtkFloatArray。由于我们的包装器 class 隐式继承 vtkObject 我们应该遵循 vtk 的继承结构以使其在 vtk 中可用(例如 vtkNew<vtkEigenFloatArray>)同时保持 pimp 习语(这就是为什么你可以看到许多 vtk 方法和创建的 class 中的宏,这是通过类比 vtkFloatArray 源代码完成的)。

我们将Eigen::MatrixXf* eigArr成员变量添加到我们的vtkEigenFloatArray class.

我们还添加了 Eigen::MatrixXf* GetEigenArray() 方法 returns Eigen::MatrixXf* eigArr 指针和 setter void SetEigenArray(Eigen::MatrixXf* arr) 以便可以使用内部 Eigen::MatrixXf来自外部的对象已创建 class.

值得注意的是,vtkEigenFloatArray 及其受保护的 Eigen::MatrixXf* eigArr 对象共享同一个缓冲区。每次调用 void UpdateEigenBuffer() 函数时都会设置一次。此外,每次从创建的 class 外部调整 Eigen::MatrixXf* eigArr 的大小时,必须调用 void UpdateEigenBuffer() 函数 来更新 vtkFloatArray-继承的大小class.

此外,用户 绝不能从外部删除 Eigen::MatrixXf* eigArr,因为它是在 vtkEigenFloatArray 的析构函数中删除的。

建议解决方案的缺点:

删除主vtkEigenFloatArray 对象后,指针Eigen::MatrixXf* eigArr 将不等于nullptr。因此,当你没有 holder vtkEigenFloatArray 对象时,你无法检查 Eigen::MatrixXf* eigArr 是否还活着。

#include <vtkNew.h>
#include <vtkFloatArray.h>
#include <Eigen/Dense>
#include <iostream>

class vtkEigenFloatArray : public vtkFloatArray
{
public:
  static vtkEigenFloatArray* New();
  vtkTypeMacro(vtkEigenFloatArray, vtkFloatArray);


  void PrintSelf(ostream& os, vtkIndent indent) override
  {
    this->RealSuperclass::PrintSelf(os, indent);
  }

  // This macro expands to the set of method declarations that
  // make up the interface of vtkAOSDataArrayTemplate, which is ignored
  // by the wrappers.
#if defined(__VTK_WRAP__) || defined(__WRAP_GCCXML__)
  vtkCreateWrappedArrayInterface(float);
#endif

  /**
   * A faster alternative to SafeDownCast for downcasting vtkAbstractArrays.
   */
  static vtkEigenFloatArray* FastDownCast(vtkAbstractArray* source)
  {
    return static_cast<vtkEigenFloatArray*>(Superclass::FastDownCast(source));
  }

  /**
   * Get the minimum data value in its native type.
   */
  static float GetDataTypeValueMin() { return VTK_FLOAT_MIN; }

  /**
   * Get the maximum data value in its native type.
   */
  static float GetDataTypeValueMax() { return VTK_FLOAT_MAX; }
  
  /**
   * Deletes previous pointer and sets new
   */
  void SetEigenArray(Eigen::MatrixXf* arr)
  {
    delete eigArr;
    eigArr = arr;
    UpdateEigenBuffer();
  }

  /**
   * Don't delete this pointer!
   */
  Eigen::MatrixXf* GetEigenArray()
  {
    return eigArr;
  }

  /**
   * Must be manually called every time Eigen::MatrixXf resized
   */
  void UpdateEigenBuffer()
  {
    this->SetArray(eigArr->data(), eigArr->size(), 1); // must be set to 1 or in destructor runtime error
  }

protected:
  vtkEigenFloatArray()
  {
    eigArr = new Eigen::MatrixXf();
  }

  ~vtkEigenFloatArray() override
  {
    delete eigArr;
    eigArr = nullptr;
  }
  
  Eigen::MatrixXf* eigArr;

private:
  typedef vtkAOSDataArrayTemplate<float> RealSuperclass;

  vtkEigenFloatArray(const vtkEigenFloatArray&) = delete;
  void operator=(const vtkEigenFloatArray&) = delete;

  friend class vtkNew<vtkEigenFloatArray>;
};

vtkStandardNewMacro(vtkEigenFloatArray);


int main(int argc, char *argv[]) {
  /* General usage */
  vtkNew<vtkEigenFloatArray> arr;
  Eigen::MatrixXf* M = arr->GetEigenArray();
  M->resize(2, 2);
  *M << 1, 2, 3, 4;
  arr->UpdateEigenBuffer();

  std::cout << "Eigen M:" << std::endl;
  std::cout << *M << std::endl;

  std::cout << "VTK array:" << std::endl;
  for(int i = 0; i < arr->GetSize(); i++)
    std::cout << arr->GetValue(i) << std::endl;

  arr->Allocate(5);
  std::cout << "Eigen M:" << std::endl;
  std::cout << *M << std::endl;

  std::cout << "VTK array:" << std::endl;
  for(int i = 0; i < arr->GetSize(); i++)
    std::cout << arr->GetValue(i) << std::endl;

  /* Test `SetEigenArray` */
  Eigen::MatrixXf* MM = new Eigen::MatrixXf();
  MM->resize(2,2);
  *MM << 11, 22, 33, 44;

  arr->SetEigenArray(MM);

  std::cout << "Eigen MM:" << std::endl;
  std::cout << *MM << std::endl;

  std::cout << "VTK array:" << std::endl;
  for(int i = 0; i < arr->GetSize(); i++)
    std::cout << arr->GetValue(i) << std::endl;

  return 0;
}