我正在编写一个程序,可以选择显示我正在处理的算法的输出 - 这是通过更改头文件中定义的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超出范围,因此显然无法编译。我不想在条件之前声明对象,因为它是一个大对象,并且它需要可用于代码中的多个点(我不能只有一个条件语句,其中一切都已完成)。
这样做的首选方式是什么?
答案 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);
}
和对象生命周期不跨越范围边界。