[英]How to ensure that a method is executed only once for the lifetime of that object?
class MyObj{
public:
void myFunc(){
//ToBeExecutedJustOnce
}
};
我有一个函数,我想在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
选项:
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...
}
}
};
还有更好的建议吗?
编辑:我不关心线程安全!
编辑:我不想在构造函数中执行该方法,只是因为该方法可能需要在对象的生命周期中稍后执行....
使用std::once_flag
。 它不能从其他方法重置(如果你不能相信同一类的其他方法,你的开发过程非常有问题),易于使用,如果你关心它,它甚至是线程安全的。 在单线程程序中它可能效率稍低。
#include <mutex>
class MyObj {
public:
void MyFunc() {
std::call_once(initFlag, [=] {
//Do stuff...
});
}
private:
std::once_flag initFlag;
};
我没有看到选项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";
}
}
};
现场演示 。
与选项1一样,您应该考虑您想要的复制/移动行为。 例如,是否应该初始化已初始化的MyObj
的副本?
我看到三个合理的选择:
bool
成员变量。 bool
成员变量 这将是我的第一选择。 当然,让它变得私密。 如果你的班级有这么多其他数据字段,那么添加这个新成员会显得很痛苦,那么这可能是整个班级设计糟糕的一个标志。
通常可以通过将类拆分为两个来完全避免init()
方法:在调用init()
之前包含构造数据的类A
和在构造时初始化的类B
这样,您可以查看对象是否仅按其类型进行初始化。
这个类看起来有点像这样:
class InitFlag
{
public:
void set()
{
isSet_ = true;
}
operator bool() const
{
return isSet_;
}
private:
bool isSet_ = false;
};
这样,成员函数就不会轻易搞乱你的旗帜。 作为类的作者,您应该能够足够信任您的成员函数,除非它们被称为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;
};
这有几个安全优势:
isInit_
。 doInit()
,只要它们不public
或protected
(这将是非常讨厌的)。 但是,他们可以而且必须实现此功能。 assert()
将触发,否则静态保证doInit()
函数不会被多次调用。 init()
函数是公共的,那么您可以使用InitializeBase
的protected
或private
属性派生。 显而易见的缺点是设计更复杂,您可以获得额外的虚函数调用。 出于这个原因,NVI成语已经引起了一些争议。
这是一个包含类中函数的变体。
调用该函数后,它将替换为不执行任何操作的函数。
const std::function<void()> nop = [](){};
class Once
{
public:
Once(std::function<void()> f) : m_function(f) {}
void operator()()
{
m_function();
m_function = nop;
}
private:
std::function<void()> m_function;
};
class Foo
{
public:
Foo(int x)
: m_function([this](){m_x += 1;}),
m_x(x) {}
int get() const { return m_x; }
void dostuff() { m_function(); }
private:
int m_x;
Once m_function;
};
int main()
{
Foo f(0);
cout << f.get() << endl; // 0
f.dostuff();
cout << f.get() << endl; // 1
f.dostuff();
cout << f.get() << endl; // 1
}
molbdnilo的答案非常好,并且与我的想法一致。 我改变了一些我个人认为更具惯用性的东西。
#include <iostream>
#include <functional>
class Once
{
bool isDone = false;
public:
void exec(std::function<void()> function)
{
if (!isDone)
{
function();
isDone = true;
}
}
};
class MyObj {
Once once = Once();
public:
void myFunc()
{
once.exec( []{
std::cout << "Hello, world!";
// do some stuff
});
}
};
int main()
{
MyObj foo = MyObj();
foo.myFunc();
foo.myFunc();
foo.myFunc();
}
顶部的解决方案非常好,但对于一个有趣的特殊情况,这可能是更好的解决方案。
我假设该方法只执行一次,因为它修改了类的状态。 对于该方法初始化类的某些部分的特殊情况,我认为最好使用optional
boost::optional
或std::optional
或std::experimental::optional
,具体取决于您可以使用的内容:
#include <boost/optional.hpp>
class X
{
public:
void init()
{
if( ! _x )
{
_x.reset( 5 );
}
}
private:
boost::optional<int> _x;
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.