如何使用其他类作为类的成员?

时间:2019-06-15 14:15:08

标签: c++ class

我有一个继承自Renderer的以下类,该类在特定点调用派生的setupdraw方法。

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中的ShaderProgramApplication类,考虑到我不希望这些类调用其析构函数,并且需要在其他地方使用它们像draw。我应该使用new并将其放到堆上吗?我应该使用指针吗?

编辑

Application app{ SCR_WIDTH, SCR_HEIGHT };

app.run();

成功初始化应用程序后,将在抽象类setup中定义的run内部调用Renderer方法。

2 个答案:

答案 0 :(得分:1)

在我的回答中,我假设您无法更改Renderer类或使用它的模式(构造从Application派生的Renderer的实例,然后调用其run(),依次调用Application::setup())。

首先,您必须认识到Application的成员是在构造Application时构造的。您的构造函数可能

Application(int windowWidth, int windowHeight)
    : Renderer{ windowWidth, windowHeight }, currentFrame(0), lastFrame(0) {};

可能不会在其初始化程序列表中列出成员nanoSuitmodelShaders,但是该标准要求无论如何都要构造它们。这将使用其默认构造函数(ModelShaderProgram的构造函数,不能接受任何参数)。如果这些类型没有此类构造函数(或无法访问),这将是可诊断的错误-换句话说,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_ptrModel的实际实例的构造(即,在构造{{ 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必须执行nanoSuitmodelShaders.operation())。

由于此时您正在为modelShaders->operation()构建(*modelShaders).operation()ShaderProgram的实例(而不是创建用于初始化现有对象的临时对象),所以不会发生额外的析构函数调用。 / p>

所有这些都还假设在调用Model之前不会使用ApplicationmodelShaders。这是“不能吃蛋糕也不能吃”的假设-延迟实际对象的构造也意味着延迟使用对象的时间。

答案 1 :(得分:0)

您可以将ModelShaderProgram设置为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的析构函数