Open3D C++系列教程(三)关于程序异常退出的探讨
前置
Open3D C++系列教程 (一)环境搭建
Open3D C++系列教程 (二)第一个GUI窗口
文章目录
1. 发现问题
如果你真的执行了教程(二)中的代码,细心的你可能会发现程序在最后异常退出了。
使用Release版o3d并生成RelWithDebInfo或使用Debug版o3d生成Debug进行调试,会发现如下的报错。
简单来说,就是程序最后死在了智能指针的析构上。
如果你继续往下探究,会发现在析构函数中应该是访问了已经被释放的对象。
2. 解决问题
2.1 方法一:将对窗口和场景所有的操作都放到一个作用域内(即一对花括号内)
注意这个地方我们将创建窗口和添加几何体的全部代码都放在一个花括号内,所有的shared_ptr
在被添加到窗口后,在离开作用域时会被释放,从而确保所有的shared_ptr
不会活到最后,从而可以避免程序的异常退出。
// ----------------------------------------------------
// ----------------------创建可视化窗口-----------------
// ----------------------------------------------------
{
auto win = std::make_shared<gui::Window>("Open3D", 1920 / 2, 1080 / 2);
// 创建场景
auto main_scene = std::make_shared<gui::SceneWidget>();
main_scene->SetScene(std::make_shared<rendering::Open3DScene>(win->GetRenderer()));
// 添加场景到窗口
win->AddChild(main_scene);
// 添加窗口到Application
instance.AddWindow(std::move(win));
// ----------------------------------------------------
// ----------------------添加几何体数据-----------------
// ----------------------------------------------------
for (int i = 0; i < 10; ++i)
{
auto mesh = TriangleMesh::CreateSphere(1.0, 4);
mesh->PaintUniformColor(rgb(rand() % 255, rand() % 255, rand() % 255));
mesh->Translate({rand() % 20 - 10.0, rand() % 20 - 10.0, rand() % 10 - 5.0});
main_scene->GetScene()->AddGeometry(
"mesh_" + std::to_string(i), // geometry name
mesh.get(), // geometry
rendering::MaterialRecord()); // material
}
// 设置相机
auto bounding_box = main_scene->GetScene()->GetBoundingBox();
main_scene->SetupCamera(60, bounding_box, bounding_box.GetCenter().cast<float>());
}
instance.Run();
2.2 方法二:使用weak_ptr
weak_ptr
并不持有数据,不会使shared_ptr
的引用计数+1。我们为窗口需要的控件创建一个weak_ptr
,所有控件的shared_ptr
任然需要创建在作用域内,并在作用域内赋值给对应的weak_ptr
。这样仅需将窗口的创建代码放在作用域内,而将其他的几何数据处理和添加的部分代码放在作用域外,通过weak_ptr.lock()
来访问窗口控件的shared_ptr
。
std::weak_ptr<gui::Window> weak_win;
std::weak_ptr<gui::SceneWidget> weak_main_scene;
{
auto win = std::make_shared<gui::Window>("Open3D", 1920 / 2, 1080 / 2);
weak_win = win;
// 创建场景
auto main_scene_ = std::make_shared<gui::SceneWidget>();
main_scene_->SetScene(std::make_shared<rendering::Open3DScene>(win->GetRenderer()));
weak_main_scene = main_scene_;
// 添加场景到窗口
win->AddChild(main_scene_);
// 添加窗口到Application
instance.AddWindow(std::move(win));
}
// ----------------------------------------------------
// ----------------------添加几何体数据-----------------
// ----------------------------------------------------
for (int i = 0; i < 10; ++i)
{
auto mesh = TriangleMesh::CreateSphere(1.0, 4);
mesh->PaintUniformColor(rgb(rand() % 255, rand() % 255, rand() % 255));
mesh->Translate({rand() % 20 - 10.0, rand() % 20 - 10.0, rand() % 10 - 5.0});
weak_main_scene.lock()->GetScene()->AddGeometry(
"mesh_" + std::to_string(i), // geometry name
mesh.get(), // geometry
rendering::MaterialRecord()); // material
}
// 设置相机
auto bounding_box = weak_main_scene.lock()->GetScene()->GetBoundingBox();
weak_main_scene.lock()->SetupCamera(60, bounding_box, bounding_box.GetCenter().cast<float>());
instance.Run();
3. 运行结果
通过上面的修改,程序就应该能够正确退出了。
完整代码下载
如果不想抄代码,或者想要获取直接获取源代码文件,可以通过链接下载。那么代价是什么呢?
注:程序退出方式
在之后的代码中,你可能会发现使用ESC时程序会异常退出,使用窗口右上角的X又能够正确退出,相当的诡异!