我有一个继承自Renderer的以下类,该类在特定点调用派生的setup
和draw
方法。
class Application : public Renderer
{
private:
float currentFrame, lastFrame;
Model nanoSuit;
ShaderProgram modelShaders;
public:
Application(int windowWidth, int windowHeight)
: Renderer{ windowWidth, windowHeight }, currentFrame(0), lastFrame(0) {};
virtual ~Application();
virtual void setup();
virtual void draw();
};
然后我有这个setup
方法:
void Application::setup()
{
... // shortened
modelShaders = ShaderProgram{ modelShdrs };
... // shortened
nanoSuit = Model{ modelPath.generic_string() };
}
我应该如何使用Model
中的ShaderProgram
和Application
类,考虑到我不希望这些类调用其析构函数,并且需要在其他地方使用它们像draw
。我应该使用new
并将其放到堆上吗?我应该使用指针吗?
编辑
Application app{ SCR_WIDTH, SCR_HEIGHT };
app.run();
成功初始化应用程序后,将在抽象类setup
中定义的run
内部调用Renderer
方法。
答案 0 :(得分:1)
在我的回答中,我假设您无法更改Renderer
类或使用它的模式(构造从Application
派生的Renderer
的实例,然后调用其run()
,依次调用Application::setup()
)。
首先,您必须认识到Application
的成员是在构造Application
时构造的。您的构造函数可能
Application(int windowWidth, int windowHeight) : Renderer{ windowWidth, windowHeight }, currentFrame(0), lastFrame(0) {};
可能不会在其初始化程序列表中列出成员nanoSuit
和modelShaders
,但是该标准要求无论如何都要构造它们。这将使用其默认构造函数(Model
和ShaderProgram
的构造函数,不能接受任何参数)。如果这些类型没有此类构造函数(或无法访问),这将是可诊断的错误-换句话说,Application
构造函数的代码将无法编译。
在您的问题中,描述了一个setup()
函数,我在此解释一下
void Application::setup() { modelShaders = ShaderProgram{ modelShdrs }; nanoSuit = Model{ modelPath.generic_string() }; }
此函数实际上构造了一个ShaderProgram
对象,并将其分配给modelShaders
。但是,该分配已实现(在ShaderProgram
中),结果是存在ShaderProgram
的两个实例(名为Application
的{{1}}的成员,而临时的在右侧创建)。必须销毁该临时文件,因此(无论其成员是复制还是移动到modelShaders
中),都必须调用其析构函数。
分配modelShaders
的语句也发生类似的情况。
从这里开始的选择取决于您的课程支持的操作。在nanoSuit
的开头,Application::setup()
和modelShaders
都是默认构造的。
因此,无需构造临时对象来对其进行初始化,而是需要直接设置这些对象的状态。例如;
nanoSuit
很明显,这仅在您的 modelShaders.setModelShdrs(modelShdrs);
nanoSuit.setGenericString(modelPath.generic_string();};
和Model
类提供适当的成员函数来设置其状态时有效。
现在,如果ShaderProgram
和/或Model
类不支持此类操作,则需要推迟对象的构造。一种实现方法是使用ModelShader
(来自std::unique_ptr
),像这样;
<memory>
这将起作用,因为class Application : public Renderer
{
private:
float currentFrame, lastFrame;
std::unique_ptr<Model> nanoSuit;
std::unique_ptr<ShaderProgram> modelShaders;
public:
Application(int windowWidth, int windowHeight)
: Renderer{ windowWidth, windowHeight }, currentFrame(0), lastFrame(0) {};
virtual ~Application();
virtual void setup();
virtual void draw();
};
的默认构造函数在没有包含对象的情况下对其进行了初始化。
现在,我们已经竭尽全力地延迟了std::unique_ptr
和Model
的实际实例的构造(即,在构造{{ 1}})ShaderProgram
实际上需要构造它们。例如;
Application
或(完成等效设置,但到达目的地的机制不同)
setup()
由于所有这些void Application::setup()
{
modelShaders = new ShaderProgram{ modelShdrs };
nanoSuit = new Model{ modelPath.generic_string() };
}
和void Application::setup()
{
modelShaders.reset(new ShaderProgram{ modelShdrs });
nanoSuit.reset(new Model{ modelPath.generic_string() });
}
现在都是管理实际对象的(智能)指针,因此必须更改它们的操作以使用指针语法(例如,代替modelShaders
必须执行nanoSuit
或modelShaders.operation()
)。
由于此时您正在为modelShaders->operation()
构建(*modelShaders).operation()
和ShaderProgram
的实例(而不是创建用于初始化现有对象的临时对象),所以不会发生额外的析构函数调用。 / p>
所有这些都还假设在调用Model
之前不会使用Application
和modelShaders
。这是“不能吃蛋糕也不能吃”的假设-延迟实际对象的构造也意味着延迟使用对象的时间。
答案 1 :(得分:0)
您可以将Model
和ShaderProgram
设置为Singleton类,并在需要时使用它。
PFB示例:
Class Model
{
private:
Model() { } //C-tor
Model(const Model& rhs) { } //Copy Constructor
~Model() { } //D-tor
static Model* instance;
public:
static Model* getInstance()
{
if(instance == NULL)
instance = new Model();
return instance;
}
};
Model* Model::instance = NULL;
如果要使用“斯科特·迈耶斯单例方法”,则可以按以下方式使用。
class Model{
public:
static Model& getInstance(){
static Model instance;
return instance;
}
private:
Model()= default;
~Model()= default;
Model(const Model&)= delete;
Model& operator=(const Model&)= delete;
};
但是,如果使用多线程,则需要寻找线程安全的实现。
在Singleton之上,实现不是线程安全的。如果您使用的是C ++ 11和更高版本的C ++,则Scott Meyers的实现是线程安全的
现在,您也可以为ShaderProgram
使用类似的Singleton类,并且可以在函数中使用它,例如:
void Application::setup()
{
... // shortened
modelShaders = Model::getInstance()->getmodelShaders();//Required method to call
... // shortened
nanoSuit = Model::getInstance()->getGenericStrings(); //Required method to call
}
我希望以上内容能对您有所帮助,因为它不会调用Model
的析构函数