毫无疑问,很多人都熟悉Alexandrescus ScopeGuard先生模板(现在是Loki的一部分)和新版ScopeGuard11:http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012- 安德烈- Alexandrescu的系统性,错误处理-在-C
来源:https: //gist.github.com/KindDragon/4650442
在他在c ++和2012年以后的演讲中,他提到他无法找到一种方法来正确检测范围是否因异常而退出.因此,当且仅当由于异常而退出作用域时,他才能实现SCOPE_FAIL宏,该宏将执行提供的lambda(通常用于回滚代码).这将使得dismiss()成员函数不再需要,并使代码更具可读性.
既然我不像Alexandrescu先生那样天才或经验丰富,我希望实施SCOPE_FAIL并不像以下那么容易:
~ScopeGuard11(){ //destructor
if(std::uncaught_exception()){ //if we are exiting because of an exception
f_(); //execute the functor
}
//otherwise do nothing
}
Run Code Online (Sandbox Code Playgroud)
我的问题是为什么不呢?
在过去的两年里,我一直在我的项目中广泛使用智能指针(boost :: shared_ptr).我理解并欣赏他们的好处,我一般都喜欢他们.但是我越是使用它们,我就越想念C++的确定性行为,关于内存管理和RAII,我似乎更喜欢编程语言.智能指针简化了内存管理的过程并提供了自动垃圾收集等功能,但问题是一般使用自动垃圾收集和智能指针特别引入了(de)初始化顺序的某种程度的不确定性.这种不确定性使控制权远离程序员,并且正如我最近意识到的那样,它完成了设计和开发API的工作,
为了详细说明,我目前正在开发一个API.此API的某些部分要求在其他对象之前初始化或销毁某些对象.换句话说,(de)初始化的顺序有时很重要.举个简单的例子,假设我们有一个名为System的类.系统提供一些基本功能(在我们的示例中记录)并通过智能指针保存许多子系统.
class System {
public:
boost::shared_ptr< Subsystem > GetSubsystem( unsigned int index ) {
assert( index < mSubsystems.size() );
return mSubsystems[ index ];
}
void LogMessage( const std::string& message ) {
std::cout << message << std::endl;
}
private:
typedef std::vector< boost::shared_ptr< Subsystem > > SubsystemList;
SubsystemList mSubsystems;
};
class Subsystem {
public:
Subsystem( System* pParentSystem )
: mpParentSystem( pParentSystem ) {
}
~Subsystem() {
pParentSubsystem->LogMessage( "Destroying..." );
// Destroy this subsystem: deallocate memory, release resource, etc.
} …
Run Code Online (Sandbox Code Playgroud) 我试图在我的服务类中遵循RAII模式,这意味着当构造一个对象时,它会被完全初始化.但是,我遇到了异步API的困难.有问题的类的结构如下所示
class ServiceProvider : IServiceProvider // Is only used through this interface
{
public int ImportantValue { get; set; }
public event EventHandler ImportantValueUpdated;
public ServiceProvider(IDependency1 dep1, IDependency2 dep2)
{
// IDependency1 provide an input value to calculate ImportantValue
// IDependency2 provide an async algorithm to calculate ImportantValue
}
}
Run Code Online (Sandbox Code Playgroud)
我也打算在ImportantValue
getter中摆脱副作用,使其成为线程安全的.
现在,用户ServiceProvider
将创建它的实例,订阅ImportantValue
更改事件,并获得初始值ImportantValue
.这里出现了问题,初始值.由于ImportantValue
是异步计算的,因此无法在构造函数中完全初始化类.最初将此值设为null可能没问题,但是我需要在某个地方首次计算它.一个自然的地方可能是ImportantValue
吸气剂,但我的目标是使其线程安全,没有副作用.
所以我基本上坚持这些矛盾.你能帮助我并提供一些替代方案吗?在构造函数中初始化值虽然不是很好,但是没有副作用和属性的线程安全是必需的.
提前致谢.
编辑:还有一件事要补充.我正在使用Ninject进行实例化,据我所知,它不支持异步方法来创建绑定.虽然在构造函数中启动一些基于任务的操作的方法将起作用,但我无法等待其结果.
即两个下一个方法(目前为止提供的答案)将无法编译,因为返回了Task,而不是我的对象:
Kernel.Bind<IServiceProvider>().ToMethod(async ctx => await ServiceProvider.CreateAsync())
Run Code Online (Sandbox Code Playgroud)
要么
Kernel.Bind<IServiceProvider>().ToMethod(async ctx =>
{
var …
Run Code Online (Sandbox Code Playgroud) 这是来自Stroustrup的"C++编程语言"的代码,该代码实现了一个finally
我无法理解析构函数被调用的地方.
template<typename F> struct Final_action
{
Final_action(F f): clean{f} {}
~Final_action() { clean(); }
F clean;
}
template<class F>
Final_action<F> finally(F f)
{
return Final_action<F>(f);
}
void test(){
int* p=new int{7};
auto act1 = finally( [&]{delete p;cout<<"Goodbye,cruel world\n";} );
}
Run Code Online (Sandbox Code Playgroud)
我有两个问题:
根据作者的说法,delete p
只有一次被调用:当act1超出范围时.但是从我的理解:首先,act1
将使用复制构造函数初始化,然后Final_action<F>(f)
函数中的临时对象finally
被破坏,delete p
第一次调用,然后在函数结束test
时第二次act1
超出范围.我哪里弄错了?
为什么finally
需要这个功能?我不能只定义Final_action act1([&]{delete p;cout<<"Goodbye,cruel world\n"})
?那是一样的吗?
此外,如果有人能想到更好的标题,请修改当前的标题.
更新:在进一步思考之后,我现在确信析构函数可能会被调用三次.另外一个是用于在调用函数中自动生成的临时对象,void test()
用作复制构造函数的参数act1
.这可以通过-fno-elide-constructors
g ++中的选项进行验证.对于那些和我有同样问题的人,请参阅Bill Lynch在答案中指出的复制省略 …
我刚刚玩了Java文件系统API,并提供了以下函数,用于复制二进制文件.最初的源代码来自Web,但我添加了try/catch/finally子句,以确保在出现错误之前,在退出函数之前缓冲流将被关闭(因此,我的操作系统资源被释放).
我减少了功能以显示模式:
public static void copyFile(FileOutputStream oDStream, FileInputStream oSStream) throw etc...
{
BufferedInputStream oSBuffer = new BufferedInputStream(oSStream, 4096);
BufferedOutputStream oDBuffer = new BufferedOutputStream(oDStream, 4096);
try
{
try
{
int c;
while((c = oSBuffer.read()) != -1) // could throw a IOException
{
oDBuffer.write(c); // could throw a IOException
}
}
finally
{
oDBuffer.close(); // could throw a IOException
}
}
finally
{
oSBuffer.close(); // could throw a IOException
}
}
Run Code Online (Sandbox Code Playgroud)
据我所知,我不能把这两个close()
放在finally子句中,因为第一个close()
可以抛出,然后,第二个不会被执行.
我知道C#有Dispose模式,可以使用using
关键字来处理这个问题.
我甚至知道更好的C++代码(使用类似Java的API): …
我是C++业余爱好者.我正在编写一些Win32 API代码,并且有句柄和奇怪的复合分配对象.所以我想知道 - 是否有一些包装类可以使资源管理更容易?
例如,当我想加载一些数据时,我打开一个文件CreateFile()
并得到一个HANDLE
.当我完成它之后,我应该呼吁CloseHandle()
它.但是对于任何相当复杂的加载函数,将会有许多可能的退出点,更不用说例外了.
因此,如果我可以将句柄包装在某种类型的包装类中会很好,它会CloseHandle()
在执行一次离开范围后自动调用.更好的是 - 它可以进行一些引用计数,因此我可以将其传入和传出其他函数,并且只有在最后一个引用离开范围时它才会释放资源.
这个概念很简单 - 但标准库中有类似的东西吗?顺便说一下,我正在使用Visual Studio 2008,而且我不想附加像Boost这样的第三方框架.
在boost中是否有可用的模板RAII
.有像这样的类scoped_ptr
,shared_ptr
它基本上用于指针.这些类可以用于除指针之外的任何其他资源.是否有任何模板可以使用一般资源.
例如,在范围的开头获取的某些资源必须以某种方式在范围的末尾发布.获取和发布都需要一些步骤.我们可以编写一个模板,它需要两个(或者一个对象)仿函数来完成这个任务.我还没有想过如何实现这一点,我只是想知道有没有现成的方法来做到这一点
Edit:
如何在C++ 0x中支持lambda函数
以相反的顺序为变量分配内存有什么好处?
考虑这个用C++演示RAII的简单类(从我的脑海中开始):
class X {
public:
X() {
fp = fopen("whatever", "r");
if (fp == NULL)
throw some_exception();
}
~X() {
if (fclose(fp) != 0){
// An error. Now what?
}
}
private:
FILE *fp;
X(X const&) = delete;
X(X&&) = delete;
X& operator=(X const&) = delete;
X& operator=(X&&) = delete;
}
Run Code Online (Sandbox Code Playgroud)
我不能在析构函数中抛出异常.我有错误,但无法报告.这个例子非常通用:我不仅可以使用文件,还可以使用例如posix线程,图形资源,...我注意到例如维基百科RAII页面如何扫描地毯下的整个问题:http:// en .wikipedia.org /维基/ Resource_Acquisition_Is_Initialization
在我看来,RAII只有在保证无故障发生的情况下才有用.我知道这个属性的唯一资源是内存.现在在我看来,例如Boehm相当令人信服地揭穿手动内存管理的想法在任何常见情况下都是一个好主意,那么C++使用RAII的方式的优势在哪里呢?
是的,我知道GC在C++世界中有点异端;-)
我有一个线程类,我偶尔会从中获取一个指针实例变量.我希望这个访问由互斥锁保护,以便阻止线程访问此资源,直到客户端完成其指针.
我最初的方法是返回一对对象:一个是指向资源的指针,另一个是指向互斥锁上的锁定对象的shared_ptr.此shared_ptr包含对锁对象的唯一引用,因此当超出范围时,应该解锁互斥锁.像这样的东西:
void A::getResource()
{
Lock* lock = new Lock(&mMutex);
return pair<Resource*, shared_ptr<Lock> >(
&mResource,
shared_ptr<Lock>(lock));
}
Run Code Online (Sandbox Code Playgroud)
此解决方案不太理想,因为它要求客户端保留整个对象.这样的行为打破了线程的安全性:
Resource* r = a.getResource().first;
Run Code Online (Sandbox Code Playgroud)
另外,我自己实现的是死锁,我很难确定原因,所以可能还有其他问题.
我想要的是一个shared_ptr,它包含作为实例变量的锁,并将其与访问资源的方法绑定在一起.这似乎应该有一个既定的设计模式,但做了一些研究,我很惊讶地发现它很难找到.
我的问题是:
(注意我正在使用Qt的代码库,但遗憾的是在这种情况下不能使用boost.但是,涉及提升的答案仍然是普遍感兴趣的.)
raii ×10
c++ ×8
c++11 ×2
async-await ×1
boost ×1
c# ×1
destructor ×1
java ×1
loki ×1
mutex ×1
ninject ×1
scopeguard ×1
shared-ptr ×1
templates ×1