CERN ROOT的对象所有权和C++11智能指针

3

我想了解智能指针如何与ROOT对象所有权方案共存。我不用走太远就可以找到答案,看看这个:

#include <iostream>
#include <memory>
#include "TH1F.h"
#include "TFile.h"

int main()
{
  TFile f("out.root", "recreate");
  f.cd();
  std::unique_ptr<TH1F> h {new TH1F("h", "h", 100, -5, 5)};
  h->FillRandom("gaus", 10000);
  h->Write();
  f.Close();

  return 0;
}

由一个独特指针处理的直方图由当前的gDirectory所拥有。因为我在退出程序前礼貌地关闭了文件,所以这个直方图已经被ROOT内存管理器销毁了。现在在main()结束时,我的指针超出了范围,它的资源需要被释放,但它已经被释放了!我没有找到任何关于ROOT对象所有权/内存管理如何与C++11智能指针一起使用的资源。我的问题是,你是否在打开ROOT对象管理的代码中使用智能指针?你是否在你的HNEP实验中使用C++11的智能指针?

1
如果ROOT真的负责所有权,为什么你还想让它与unique_ptr一起工作呢?我认为ROOT的这个特定部分并不是设计用于与智能指针(或任何智能东西)一起使用的。 - juanchopanza
嗯,我想了解在处理ROOT对象的代码中,使用智能指针的不同实验/用户指南是什么。如果现代C++代码预计要摆脱裸指针,那么我们应该看到程序员代码中更多的智能指针。那么,我们是否应该禁止使用智能指针来处理ROOT对象?关闭ROOT对象管理?ROOT是否有其他计划来解决这个问题? - Mustafa
当然,但你只应在需要时使用智能指针。如果某个东西已经拥有了动态分配的对象,除非所有者具有某种释放机制,否则你不能尝试接管所有权。还要注意,大部分代码可能是在现代之前“设计”的。我见过的一种方法是将所有这些内存管理隐藏在更现代的接口后面,这些接口似乎不依赖于全局对象和奇怪的约定。 - juanchopanza
1
此外,如果我没记错的话,重新设计 ROOT 中那些老旧的角落以采用现代 C++ 语言习惯将会非常困难,因为它遵循最大耦合设计原则。你不能触及一个东西而不破坏其他所有东西。但是你可以将自己与整个混乱隔离开来。 - juanchopanza
据我从你的代码中看,h没有对f做出任何影响。 - ventsyv
3个回答

2

如果您正在使用std::unique_ptr,您真的希望它成为对象的唯一所有者。您可以使用h->SetDirectory关闭一个直方图在ROOT中的对象所有权:

#include <iostream>
#include <memory>
#include "TH1F.h"
#include "TFile.h"

int main()
{
  TFile f("out.root", "recreate");
  f.cd();
  std::unique_ptr<TH1F> h {new TH1F("h", "h", 100, -5, 5)};
  h->SetDirectory(0);
  h->FillRandom("gaus", 10000);
  h->Write();
  f.Close();

  return 0;
}

这样,您仍然拥有所有其他直方图的ROOT对象所有权,但是您可以自己拥有此对象。


2
如果你使用TH1::AddDirectory(false),你会管理直方图,然后使用智能指针就不会有问题。

我的问题是如何使用智能指针使ROOT对象所有权“存活”,而不是关闭ROOT对象管理。 - Mustafa

0

嗯,我猜想要让unique_ptr和ROOT愉快地结合在一起,你必须使用自定义删除器。

在自定义删除器中,您必须检查直方图是否仍然存在并将其删除,否则使其成为无操作

类似于以下伪代码:

auto deleter = [](TH1F* p) { key = FindKey(p->Name); if (key) delete p };

std::unique_ptr<TH1F, decltype(deleter)> h{new TH1F("h", "h", 100, -5, 5), deleter};

可以设计更复杂的方案...


除非你怎么知道它已经被删除了? - juanchopanza
@juanchopanza 如果我没记错的话(我很久以前处理过ROOT),你必须像我建议的那样按名称检查键。如果键是非空的,则在当前上下文中,并且可以被删除。 - Severin Pappadeux
你可以像这样通过名称进行检查,但是你必须知道名称。如果p已经被删除,显然调用p->Name是无效的。如果p已经被删除,这只会导致段错误。 - Chris - Regenerate Response
更糟糕的是,即使您以其他方式存储名称,如果已删除 p,则新对象现在可以具有相同的名称,因此仍将尝试 delete p - Chris - Regenerate Response

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接