首页 > 解决方案 > 在函数超出范围后保留特征矩阵不被删除

问题描述

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活跃并且不会发生内存泄漏,请解释一下。

问候

标签: c++arrayseigenvtk

解决方案


我的解决方案基于@Peter-ReinstateMonica 的建议:创建一个包装类,其中两个对象vtkFloatArrayEigen::MatrixXf将驻留,这将使控制这两个对象的生命周期成为可能。

让我们调用包装类vtkEigenFloatArray并让它继承自vtkFloatArray. 由于我们的包装类隐式继承,我们应该遵循vtkvtkObject的继承结构,使其在 vtk 中可用(例如)。vtkNew<vtkEigenFloatArray>vtkFloatArray

我们将Eigen::MatrixXf* eigArr成员变量添加到我们的vtkEigenFloatArray类中。

我们还添加了Eigen::MatrixXf* GetEigenArray()返回Eigen::MatrixXf* eigArr指针和设置器的方法,void SetEigenArray(Eigen::MatrixXf* arr)以便可以使用Eigen::MatrixXf外部创建的类的内部对象。

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

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

建议解决方案的缺点:

当主vtkEigenFloatArray对象被删除时,指针Eigen::MatrixXf* eigArr将不等于nullptrEigen::MatrixXf* eigArr因此,当您没有持有人vtkEigenFloatArray对象时,您无法检查是否还活着。

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

推荐阅读