RAII范围内的分配
Assignment within RAII scope
问题
如何在RAII范围内初始化对象,并在该范围外使用它?
背景
-
我有一个全局锁,它可以用
lock()
和unlock()
调用。 -
我有一个类型
LockedObject
,它只能在全局锁被锁定时初始化。 -
我有一个函数
use_locked(LockedObject &locked_object)
,需要在全局锁未锁定的情况下调用它。
使用场景为
lock();
LockedObject locked_object;
unlock();
use_locked(locked_object);
RAII
由于各种原因,我转向了全局锁的RAII封装。我想在任何地方都使用它,主要是因为创建LockedObject
可能会失败。
问题是
{
GlobalLock global_lock;
LockedObject locked_object;
}
use_locked(locked_object);
失败,因为locked_object
是在内部作用域中创建的。
示例
设置(大部分不重要(:
#include <assert.h>
#include <iostream>
bool locked = false;
void lock() {
assert(!locked);
locked = true;
}
void unlock() {
assert(locked);
locked = false;
}
class LockedObject {
public:
LockedObject(int i) {
assert(locked);
std::cout << "Initialized: " << i << std::endl;
}
};
void use_locked(LockedObject locked_object) {
assert(!locked);
}
class GlobalLock {
public:
GlobalLock() {
lock();
}
~GlobalLock() {
unlock();
}
};
原始、非RAII方法:
void manual() {
lock();
LockedObject locked_object(123);
unlock();
use_locked(locked_object);
}
坏的RAII方法:
/*
void raii_broken_scoping() {
{
GlobalLock global_lock;
// Initialized in the wrong scope
LockedObject locked_object(123);
}
use_locked(locked_object);
}
*/
/*
void raii_broken_initialization() {
// No empty initialization
// Alternatively, empty initialization requires lock
LockedObject locked_object;
{
GlobalLock global_lock;
locked_object = LockedObject(123);
}
use_locked(locked_object);
}
*/
和一个main
函数:
int main(int, char **) {
manual();
// raii_broken_scoping();
// raii_broken_initialization;
}
值得一提的是,在Python中,我会做:
with GlobalLock():
locked_object = LockedObject(123)
我想要同等的。我在回答中提到了我目前的解决方案,但感觉很笨拙。
要执行的具体(但经过简化(代码如下。使用我当前基于lambda的调用:
boost::python::api::object wrapped_object = [&c_object] () {
GIL lock_gil;
return boost::python::api::object(boost::ref(c_object));
} ();
auto thread = std::thread(use_wrapped_object, c_object);
带有
class GIL {
public:
GIL();
~GIL();
private:
GIL(const GIL&);
PyGILState_STATE gilstate;
};
GIL::GIL() {
gilstate = PyGILState_Ensure();
}
GIL::~GIL() {
PyGILState_Release(gilstate);
}
boost::python::api::object
s必须与GIL一起创建,而线程则必须
在堆上分配对象并使用一些指针:
std::unique_ptr<LockedObject> locked_object;
{
GlobalLock global_lock;
locked_object.reset(new LockedObject());
}
use_locked(locked_object);
从我的角度来看,这里有一个完整的选项列表。optional
将是我要做的:
所提出的后C++1y optional
将解决您的问题,因为它允许您在声明后构造数据,基于堆的unique_ptr
解决方案也是如此。滚你自己的,或从boost
偷ot
一个"在作用域结束时运行"RAII函数存储程序(带有"提交"(也可以让这个代码不那么疯狂,让你的锁在它们的作用域内手动脱离也可以。
template<class F>
struct run_at_end_of_scope {
F f;
bool Skip;
void commit(){ if (!Skip) f(); Skip = true; }
void skip() { Skip = true; }
~run_at_end_of_scope(){commit();}
};
template<class F>
run_at_end_of_scope<F> at_end(F&&f){ return {std::forward<F>(f), false}; }
然后:
auto later = at_end([&]{ /*code*/ });
您可以later.commit();
或later.skip();
提前运行代码或跳过运行。
使RAII锁定类具有move构造函数将允许您在另一个范围中进行构造,并通过move返回(可能被省略(。
LockedObject make_LockedObject(){
GlobalLock lock;
return {};
}
我目前的解决方案是使用匿名函数:
void raii_return() {
LockedObject locked_object = [&] () {
GlobalLock global_lock;
return LockedObject(123);
} ();
use_locked(locked_object);
}
这种方法的优点是避免了指针,而且由于省略了复制,所以速度应该很快。
一个缺点是LockedObject
不一定支持复制(在这种情况下,use_locked
会引用(。
- 为什么在全局范围内使用"extern int a"似乎不行?
- 错误:未在此范围内声明'reverse'
- 并行用于C++17中数组索引范围内的循环
- 求出有多少个数字是完美平方,而sqrt()是L,R范围内的素数
- 不计算一个范围内的完美数
- 错误:"imread"未在此范围内声明
- 我在范围内未声明的错误类有问题
- 如何在cpp中使用地图显示给定日期范围内(在下面的问题中)的费率?
- 我有一个数组,我想输入一个范围,然后找到范围内所有偶数的总和?
- 未在此范围内声明错误 'xy'
- 在C++中使用变量而不是"#define"来指定数组大小是不是一种糟糕的做法?(C错误:在文件范围内
- 命名空间范围内的外部 - GCC vs clang vs msvc
- 如何改进一堆在已知值范围内评估变量的 else-if 条件?
- 如何仅使用 While 循环在给定范围内找到可被 7 整除的计数整数
- Socklen_t未在此范围内声明
- 错误:'[' 之前预期的非限定 id 和错误:'users'未在此范围内声明
- 查找给定范围内最长连续 1 的频率
- "Main"已在当前范围内声明
- 无法在命名空间范围内分配变量
- RAII范围内的分配