class MyObj{
public:
void myFunc(){
//ToBeExecutedJustOnce
}
};
Run Code Online (Sandbox Code Playgroud)
我有一个函数,我想在生命周期中只执行一次MyObj.可能有许多实例MyObj,并且每个实例应该能够执行一次该功能.所以,如果我有:
MyObj first;
MyObj second;
MyObj third:
first.myFunc(); // Should execute
second.myFunc(); // Should execute
third.myFunc(); // Should execute
first.myFunc(); // Should not execute
second.myFunc(); // Should not execute
third.myFunc(); // Should not execute
Run Code Online (Sandbox Code Playgroud)
选项:
MyObj可以访问它并更改它.我找到的唯一解决方案是MyObj从另一个类继承
MyOtherObj{
private:
bool _isInit = false;
public:
bool isInit(){
bool ret = true;
if (!_isInit){
ret = false;
_isInit = true;
}
return ret;
}
};
class MyObj : public MyOtherObj {
public:
void MyFunc(){
if (!isInit()){
//Do stuff...
}
}
};
Run Code Online (Sandbox Code Playgroud)
还有更好的建议吗?
编辑:我不关心线程安全!
编辑:我不想在构造函数中执行该方法,只是因为该方法可能需要在对象的生命周期中稍后执行....
Arn*_*gel 47
使用std::once_flag.它不能从其他方法重置(如果你不能相信同一类的其他方法,你的开发过程非常有问题),易于使用,如果你关心它,它甚至是线程安全的.在单线程程序中它可能效率稍低.
#include <mutex>
class MyObj {
public:
void MyFunc() {
std::call_once(initFlag, [=] {
//Do stuff...
});
}
private:
std::once_flag initFlag;
};
Run Code Online (Sandbox Code Playgroud)
Chr*_*rew 22
我没有看到选项1有什么问题.如果一个类有这么多的责任,另一个函数可能会意外地弄乱is_init成员变量,那么这个类可能会变得更小.
但是,如果你想封装到另一个不易出错的类中,而不是使用继承,我建议你使用组合:
class FirstTime {
bool first_time = true;
public:
bool operator()(){
if (!first_time)
return false;
first_time = false;
return true;
}
};
class MyObj {
FirstTime first_time;
public:
void myFunc(){
if (first_time()){
std::cout << "First time!\n";
}
}
};
Run Code Online (Sandbox Code Playgroud)
现场演示.
与选项1一样,您应该考虑您想要的复制/移动行为.例如,初始化的副本是否应MyObj被视为初始化?
我看到三个合理的选择:
bool成员变量.bool成员变量这将是我的第一选择.当然,让它变得私密.如果你的班级有这么多其他数据字段,那么添加这个新成员会显得很痛苦,那么这可能是整个班级设计糟糕的一个标志.
通常init()可以通过将类拆分为两个来完全避免方法:A在调用之前包含构造数据init()的类和B在构造时初始化的类.这样,您可以查看对象是否仅按其类型进行初始化.
这个类看起来有点像这样:
class InitFlag
{
public:
void set()
{
isSet_ = true;
}
operator bool() const
{
return isSet_;
}
private:
bool isSet_ = false;
};
Run Code Online (Sandbox Code Playgroud)
这样,成员函数就不会轻易搞乱你的旗帜.作为类的作者,您应该能够完全信任您的成员函数,除非它们被调用,否则它们不会设置此标志init().
您可以使用init()公共和非虚拟功能创建基类.此函数检查,如果init()之前已调用过,则调用私有纯虚doInit()函数,该函数应该进行实际初始化并在此之后设置init标志.它看起来像这样:
class InitializeBase
{
public:
virtual ~InitializeBase() = default;
bool isInit() const
{
return isInit_;
}
void init()
{
assert( !isInit() );
doInit();
isInit_ = true;
}
private:
virtual void doInit() = 0;
bool isInit_ = false;
};
Run Code Online (Sandbox Code Playgroud)
这有几个安全优势:
isInit_.doInit(),只要它们不成功public或protected(这将是非常讨厌的).但是,他们可以而且必须实现此功能.doInit(),静态保证函数不会被多次调用,除非assert()会触发.init()函数是公共的,那么您可以使用protected或从中派生private属性InitializeBase.显而易见的缺点是设计更复杂,您可以获得额外的虚函数调用.出于这个原因,NVI成语已经引起了一些争议.