向上移动对象层次结构

Going up the object hierarchy

您好,我正在处理的程序中有一些不错的对象树层次结构。我遇到了从下到上沟通的问题。我现在如何设置它是在每个构造函数中我传递一个对创建新对象的对象的引用。简单的结构如下所示:
[控制器] -> [世界] -> [对象]

向上一层(从世界到控制器或从对象到世界)是可以的。但是问题开始出现的地方是当我尝试上升 2 层时。 这是我如何设置的简化结构:

Controller.h:

#include "World.h"

Class Controller {
public:
   Controller() {
      _worlds.push_back(World(*this));
   )

   void update() { // Called on a loop from main program loop
      _worlds[0].update(); // Calls update of active world, in this case world[0]
   }

   vector<World> _worlds;

   Camera _camera; // class with checkIfInView function
}

World.h:

#Include "Object.h"

Class Controller;

Class World {
   World(Controller& ref) : _controller(ref) {
      _objects.push_back(Object(*this));
      _controller._camera.doStuff(); // works OK
   }

   void update() {
      for (auto& i : _objects)
         i.update();
   }

   vector<Object> _objects;
   Controller& _controller;
}

Object.h:

Class World;

Class Object {
   Object(World& ref) : _world(ref) {}
   void update();

   World& _world;
}

Object.cpp:

#include "Controller.h"
#include "World.h"

void Object::update() {
   _world._controller._camera.checkIfInView(*this); // Read access violation
}

控制器持有一个负责显示内容的相机对象。我需要的是一种让对象调用 checkIfInView 以了解它们是否应该呈现的方法。有没有其他方法可以做到这一点或解决它?

编辑:更新代码。

问题

让我们看看您的漂亮链,从 Controller 构造函数开始。因为它是层次结构的顶级对象,所以它是构造的开始。我想在 main() 你有类似

的东西
Controller c;  

这将导致调用构造函数:

 Controller() {
    _worlds.push_back(World(*this));    // !!! 
 }

World(*this) 将创建一个新的临时世界,您将把它推入控制器的世界向量中。临时对象仅在它出现的表达式的时间内存在。

然后临时世界将由

构建
World(Controller& ref) : _controller(ref) {  // ref to controller is kept 
    _objects.push_back(Object(*this));  // ouch!!! 
    _controller._camera.doStuff(); // works OK
}

现在将创建一个指向 *this world 的对象。哎哟!!还记得那个世界是暂时的吗?在构造结束时,它将被删除,以便所有对象都将引用一个不再存在的 C++ 对象,因此在您的情况下恰好产生分段错误的 UB。

解决方案的开始

你的设计很精致。如果找不到更安全的设计模式,请三思。如果您仍然想朝这个方向努力,请避免使用临时项创建对象:而是创建动态分配的对象。

Controller() {
    _worlds.push_back(*new World(*this));    // !!! risk of leakage 
} 

下一步是使用指针而不是引用:

Controller() {
    _worlds.push_back(new World(*this));    // risk of leakage 
} 

当然,您需要相应地更改其余代码才能使用指针。

下一步是选择共享指针:这避免了泄漏的风险:

Controller() {
    _worlds.push_back(make_shared<World>(*this));    // risk of leakage 
} 

在你的代码的改编中,你需要在你的向量中区分 shared_ptr,它指的是对象,weak_ptr 指的是 parten 对象,以表明父级现在由子级共享,但由另一个对象共享。

更好的解决方案?

我警告你这不是小菜一碟。一旦有了指针,就需要处理每个 class 的 rule of 3

许多问题源于:

1)嵌套构造->可能值得考虑构建器设计模式 2)混合静态对象和动态创建对象的风险,永远不知道哪种是父对象。 -> 可能值得使用 protected/private 构造函数并使用工厂方法来确保所有对象始终是动态对象。