在游戏引擎布局中使用共享的ptr? 我在C++中编写了一个小游戏引擎,它运行正常,但不是最好的性能。我也学到了很多,现在我想重做。但上一次,我大量使用shared_ptr作为类,比如gui纹理和模型包装器,它们保持3d位置和opengl vao。我听说不应该真正使用shared_ptr,但我真的不知道如何用其他方法。以下是类似布局的伪代码:
这段代码不完整或不起作用,但我想分享我的布局想法在游戏引擎布局中使用共享的ptr? 我在C++中编写了一个小游戏引擎,它运行正常,但不是最好的性能。我也学到了很多,现在我想重做。但上一次,我大量使用shared_ptr作为类,比如gui纹理和模型包装器,它们保持3d位置和opengl vao。我听说不应该真正使用shared_ptr,但我真的不知道如何用其他方法。以下是类似布局的伪代码:,c++,game-engine,shared-ptr,C++,Game Engine,Shared Ptr,这段代码不完整或不起作用,但我想分享我的布局想法 // d3 stands for 3d class D3Model { VAO vao; bool useBloom; unsigned int texture; // ... and so on static std::vector<std::shared_ptr<D3Model>> models; // current idea, approach 1 static s
// d3 stands for 3d
class D3Model {
VAO vao;
bool useBloom;
unsigned int texture;
// ... and so on
static std::vector<std::shared_ptr<D3Model>> models; // current idea, approach 1
static std::vector<D3Model> models1; // different approach (2)
D3Model() {
models.push_back(shared_from_this()); // app.1
models1.push_back(*this);
}
}
// main file
int main() {
std::shared_ptr<D3Model> model = std::make_shared<D3Model>();
model->setTexture(0); // ignore the non sense content
D3Model model1;
model1.setTexture(0); // would not get updated in the vector
while(true) {
model->increaseRotation(1);
model1.increaseRotation(1);
Renderer::render();
}
}
// different file
class Renderer {
static void render() {
for(const auto& all : D3Model::models) {
// render model
}
for(const auto& all : D3Model::models1) {
// render model1, would not have increased rotation
}
}
}
要了解更多信息,请想象发动机的以下用法:
在开始屏幕上点击play后,引擎从文件加载一些3d模型并存储它们。其中一个每帧旋转一次,每个帧渲染一次。由于玩家加入的游戏性发生了变化,可能之后必须加载其他一些模型。。。。然后,当用户返回主菜单时,它们都会被删除。方法2的问题是,每当模型旋转增加时,我都必须更新向量,而方法1会自动处理。然而,我听说共享ptr对性能不利
在这种情况下,shared_ptr是最好的解决方案还是应该只使用普通对象?但我的问题是如何将更改应用到对象(例如setTexture),而不必更新向量内容。原始指针也不是很好。这样的事情是怎么做的?
谢谢,虚幻好的,你可以像我一样创建自己的refconner类:
/// RefCounter.hpp
#pragma once
class RefCounter {
public:
RefCounter();
RefCounter(const RefCounter&);
RefCounter& operator=(const RefCounter&);
void grab();
usize reference() const;
bool release();
protected:
virtual void finalize();
virtual ~RefCounter();
private:
usize _reference;
};
template<typename T>
struct ref {
inline ref() : class_ptr (nullptr) {}
inline ref(T* obj) : class_ptr (obj) { if(class_ptr) class_ptr->grab(); }
inline ref(const ref& other) : class_ptr (other.class_ptr) { if(class_ptr) class_ptr->grab(); }
inline ref& operator=(T* obj) {
if(obj)
obj->grab();
if(class_ptr)
class_ptr->release();
class_ptr = obj;
return *this;
}
inline ref& operator=(const ref& other){
T* obj = other.class_ptr;
if(obj)
obj->grab();
if(class_ptr)
class_ptr->release();
class_ptr = obj;
return *this;
}
T* get() const {
return class_ptr;
}
operator T*() const {
return class_ptr;
}
T* operator->() const {
return class_ptr;
}
inline ~ref() {
if(class_ptr)
class_ptr->release();
}
private:
T* class_ptr;
};
/// RefCounter.cpp
RefCounter::RefCounter() : _reference(0){
}
RefCounter::RefCounter(const RefCounter&) : _reference(0) {
}
RefCounter& RefCounter::operator=(const RefCounter&) {
return *this;
}
void RefCounter::grab() {
_reference++;
}
usize RefCounter::reference() const {
return _reference;
}
bool RefCounter::release() {
if(_reference > 1) {
_reference--;
return false;
} else {
_reference = 0;
finalize();
return true;
}
}
void RefCounter::finalize() {
delete this;
}
RefCounter::~RefCounter() {
}
好的,您可以像我一样创建自己的refconner类:
/// RefCounter.hpp
#pragma once
class RefCounter {
public:
RefCounter();
RefCounter(const RefCounter&);
RefCounter& operator=(const RefCounter&);
void grab();
usize reference() const;
bool release();
protected:
virtual void finalize();
virtual ~RefCounter();
private:
usize _reference;
};
template<typename T>
struct ref {
inline ref() : class_ptr (nullptr) {}
inline ref(T* obj) : class_ptr (obj) { if(class_ptr) class_ptr->grab(); }
inline ref(const ref& other) : class_ptr (other.class_ptr) { if(class_ptr) class_ptr->grab(); }
inline ref& operator=(T* obj) {
if(obj)
obj->grab();
if(class_ptr)
class_ptr->release();
class_ptr = obj;
return *this;
}
inline ref& operator=(const ref& other){
T* obj = other.class_ptr;
if(obj)
obj->grab();
if(class_ptr)
class_ptr->release();
class_ptr = obj;
return *this;
}
T* get() const {
return class_ptr;
}
operator T*() const {
return class_ptr;
}
T* operator->() const {
return class_ptr;
}
inline ~ref() {
if(class_ptr)
class_ptr->release();
}
private:
T* class_ptr;
};
/// RefCounter.cpp
RefCounter::RefCounter() : _reference(0){
}
RefCounter::RefCounter(const RefCounter&) : _reference(0) {
}
RefCounter& RefCounter::operator=(const RefCounter&) {
return *this;
}
void RefCounter::grab() {
_reference++;
}
usize RefCounter::reference() const {
return _reference;
}
bool RefCounter::release() {
if(_reference > 1) {
_reference--;
return false;
} else {
_reference = 0;
finalize();
return true;
}
}
void RefCounter::finalize() {
delete this;
}
RefCounter::~RefCounter() {
}
当您需要共享所有权管理时,请使用std::shared_ptr。如果是静态std::向量模型;如果工作正常,那么您可能一开始就不需要共享所有权管理。不过,如果模型需要随着时间的推移而增长,那么D3Model的指针和引用将无法保持有效,这是您在使用std::shared_ptr时不必担心的问题。所以它可能不是最好的容器。我还没有实现向量版本方法2,但我只能在每次我更改其中一个模型中的任何内容时更新向量,这显然不会很快通过某种id在向量中找到类似的对象,删除它并推送新版本。当世界加载时,模型可能会有一个大的增长,即使它应该能够在任何给定的时间加载模型以获得更灵活的使用。如果你想让它在任何时候增长,你必须假设它在任何时候都会增长。因此,您可能存在无效问题。我不明白为什么需要删除并重新添加元素来更改它们。向量是一个可变的容器,您可以直接更改元素。通过索引在向量中查找元素的时间是恒定的,开销很小。一点也不慢。这段代码不应该处理方法渲染中m的纹理为零的问题,对吗?这就是我所说的[Edit:添加了一个updateInVector方法:,其中m的纹理为1,但这是最有效的方法吗?]看起来您正在尝试自动维护实例列表。这种方法可以工作,但充满了陷阱,通常不值得付出努力。至少您需要模型来存储指向D3Model的指针。现在,每次创建一个D3模型时,都会在无限循环中将另一个模型插入到模型中。您还将遇到一个问题,即当D3Model被销毁时(例如,如果m在另一个函数中),您将有一个指向该实例的悬空指针。更不用说复制和移动构造函数和操作符了。当您需要共享所有权管理时,请使用std::shared_ptr。如果是静态std::向量模型;如果工作正常,那么您可能一开始就不需要共享所有权管理。不过,如果模型需要随着时间的推移而增长,那么D3Model的指针和引用将无法保持有效,这是您在使用std::shared_ptr时不必担心的问题。所以它可能不是最好的容器。我还没有实现向量版本方法2,但我只能在每次我更改其中一个模型中的任何内容时更新向量,这显然不会很快通过某种id在向量中找到类似的对象,删除它并推送新版本。当世界加载时,模型可能会有一个大的增长,即使它应该能够在任何给定的时间加载模型以获得更灵活的使用。如果你想让它在任何时候增长,你必须假设它在任何时候都会增长。因此,您可能存在无效问题。我不明白为什么需要删除并重新添加元素来更改它们。向量是一个可变的容器,您可以直接更改元素。通过索引在向量中查找元素的时间是恒定的,开销很小。一点也不慢。这段代码不应该处理以下问题:在
方法渲染,对吗?这就是我所说的[Edit:添加了一个updateInVector方法:,其中m的纹理为1,但这是最有效的方法吗?]看起来您正在尝试自动维护实例列表。这种方法可以工作,但充满了陷阱,通常不值得付出努力。至少您需要模型来存储指向D3Model的指针。现在,每次创建一个D3模型时,都会在无限循环中将另一个模型插入到模型中。您还将遇到一个问题,即当D3Model被销毁时(例如,如果m在另一个函数中),您将有一个指向该实例的悬空指针。更不用说复制和移动构造函数和运算符了。我看不出这对解决原始问题有什么帮助。那么也许你应该解释一下为什么std::shared_ptr不可接受,为什么这个解决方案更好,以及如何使用它。这个答案似乎和现在一样不完整。请注意,似乎只有从RefCounter派生或实现等效接口的对象才能被ref引用,这是一种侵入性设计。例如,如果没有额外的包装器,引用外部库中的类型是不可行的。它似乎也不支持移动语义。它表明,这种方法当然可以工作,但它确实有缺点,包括我提到的缺点。但我清楚地记得公开抓取和释放是多么烦人。如果我没有弄错的话,这个引擎早于c++11,c++11引入了标准的智能指针和移动语义。编辑:第一次发布是在2006年。我不认为这对解决最初的问题有什么帮助。那么也许你应该解释一下为什么std::shared_ptr不可接受,为什么这个解决方案更好,以及如何使用它。这个答案似乎和现在一样不完整。请注意,似乎只有从RefCounter派生或实现等效接口的对象才能被ref引用,这是一种侵入性设计。例如,如果没有额外的包装器,引用外部库中的类型是不可行的。它似乎也不支持移动语义。它表明,这种方法当然可以工作,但它确实有缺点,包括我提到的缺点。但我清楚地记得公开抓取和释放是多么烦人。如果我没有弄错的话,这个引擎早于c++11,c++11引入了标准的智能指针和移动语义。编辑:第一次发布是在2006年。