有条件地用c ++

时间:2017-11-17 10:01:25

标签: c++ scope heap-memory

我正在编写一个程序,可以选择显示我正在处理的算法的输出 - 这是通过更改头文件中定义的const bool VISUALIZE_OUTPUT变量来完成的。在主文件中,我希望有这种模式:

if(VISUALIZE_OUTPUT) {
    VisualizerObject vis_object;
}
...
if(VISUALIZE_OUTPUT) {
    vis_object.initscene(objects_here);
}
...
if(VISUALIZE_OUTPUT) {
    vis_object.drawScene(objects_here);
}

然而,由于vis_object超出范围,因此显然无法编译。我不想在条件之前声明对象,因为它是一个大对象,并且它需要可用于代码中的多个点(我不能只有一个条件语句,其中一切都已完成)。

这样做的首选方式是什么?

  • 在堆上声明对象并使用指针(或。)引用它 的unique_ptr)?
  • 在堆上声明对象并对其进行引用 既然它永远不会改变?
  • 其他一些替代方案?

3 个答案:

答案 0 :(得分:1)

此处的引用将无法使用,因为在声明时它应该引用一个已经存在的对象,并且生活在一个范围内,使您的所有if(VISUALIZE_OUTPUT)成为可能。简而言之,必须无条件地创建对象。

所以恕我直言,一个简单的方法是在堆上创建它并通过指针使用它 - 完成时不要忘记做delete它。好的一点是指针可以初始化为nullptr,因此可以无条件删除。

但我认为最好的方法是将所有内容封装在最高范围内创建的对象中。然后,该对象将包含在内部创建,使用并最终销毁实际vis_object的方法。这样,如果你不需要它,实际上什么都不会实现,但主程序不会被原始指针处理混乱。

答案 1 :(得分:0)

我会使用Null_object_pattern

struct IVisualizerObject
{
    virtual ~IVisualizerObject() = default;
    virtual void initscene(Object&) = 0;
    virtual void drawScene(Object&) = 0;
    // ...
};

struct NullVisualizerObject : IVisualizerObject
{
    void initscene(Object&) override { /* Empty */ }
    void drawScene(Object&) override { /* Empty */}
    // ...
};

struct VisualizerObject : IVisualizerObject
{
    void initscene(Object& o) override { /*Implementation*/}
    void drawScene(Object& o) override { /*Implementation*/}
    // ...
};

最后:

std::unique_ptr<IVisualizerObject> vis_object;
if (VISUALIZE_OUTPUT) {
    vis_object = std::make_unique<VisualizerObject>();
} else {
    vis_object = std::make_unique<NullVisualizer>();
}

// ...
vis_object->initscene(objects_here);

//...

vis_object->drawScene(objects_here);

答案 2 :(得分:0)

我会提供一些选择。所有这些都有好处和缺点。

如果无法修改VisualizerObject,正如我在评论中所指出的那样,可以通过使用预处理器来实现效果,因为预处理器不遵循范围,并且该问题专门用于控制对象的生命周期以跨越范围边界的方式。

#ifdef VISUALIZE_OUTPUT
   VisualizerObject vis_object;
#endif

#ifdef VISUALIZE_OUTPUT
    vis_object.initscene(objects_here);
#endif

编译器将诊断vis_object / #ifdef内不属于#endif的任何用法。

当然,最大的批评是使用预处理器在C ++中被认为是不好的做法。优点是即使无法修改VisualizerObject类,也可以使用该方法(例如,因为它位于没有提供源代码的第三方库中)。

但是,这是唯一具有OP请求的对象生存期越过范围边界的功能的选项。

如果可以修改VisualizerObject类,请将其设为具有两个特化的模板

template<bool visualise> struct VisualizerObject
{
     // implement all member functions required to do nothing and have no members

     VisualizerObject() {};
     void initscene(types_here) {};    
};

template<> struct VisualizerObject<true>    // heavyweight implementation with lots of members
{
     VisualizerObject(): heavy1(), heavy2() {};
     void initscene(types_here) { expensive_operations_here();};

     HeavyWeight1 heavy1;
     HeavyWeight2 heavy2;
};

int main()
{
      VisualizerObject<VISUALIZE_OUTPUT> vis_object;
      ...
      vis_object.initscene(objects_here);
      ...
      vis_object.drawScene(objects_here);
}

以上内容适用于所有C ++版本。从本质上讲,它可以通过实例化一个轻量级对象来实现,该对象具有什么都不做的成员函数,或者实例化重量级版本。

也可以使用上述方法来包装VisualizerObject

 template<bool visualise> VisualizerWrapper
 {
      //  implement all required member functions to do nothing
      //   don't supply any members either
 }

 template<> VisualizerWrapper<true>
 {
      VisualizerWrapper() : object() {};

       //  implement all member functions as forwarders

     void initscene(types_here) { object.initscene(types_here);};

      VisualizerObject object;
 }

int main()
{
      VisualizerWrapper<VISUALIZE_OUTPUT> vis_object;
      ...
      vis_object.initscene(objects_here);
      ...
      vis_object.drawScene(objects_here);
}

两种模板方法的缺点是维护 - 当向一个类添加成员函数(模板特化)时,必须向另一个类添加具有相同签名的函数。在大型团队设置中,测试/构建可能主要使用VISUALIZE_OUTPUT或另一个设置完成 - 因此很容易使一个版本与另一个版本不对齐(不同的接口)。这些问题(例如,在更改设置时失败的构建)可能会在不方便的时候出现 - 例如,在提供不同版本产品的截止日期很紧的时候。

迂腐地说,模板选项的另一个缺点是它们不符合所需的&#34;模式&#34;

中不需要if
 if(VISUALIZE_OUTPUT)
 {
    vis_object.initscene(objects_here);
 }

和对象生命周期不跨越范围边界。