在Windows API编程的时候,常常会遇到这样的场景,先创建某个对象A,然后再用这个对象去创建另外一个对象B。就像下面这段代码,正常的逻辑很简单,但是如果考虑到错误处理的话,代码就变得异常麻烦了。
pa = CreateA();
status = doStuffByA(pa);
pb = CreateBbyA(pa);
status = doStuffByB(pb);
下面这段代码单纯的依靠if-else
处理问题,可以看到代码嵌套很严重,而且在很多地方都需要调用DestoryA(pa);
,都是重复的逻辑。
bool Init(){
A* pa = nullptr;
B* pb = nullptr;
pa = CreateA();
if (pa) {
if (doStuffByA(pa)) {
pb = CreateBbyA(pa);
if (pb) {
if (doStuffByB(pb)) {
}
else {
DestoryB(pb);
DestoryA(pa);
return false;
}
}
else {
DestoryA(pa);
return false;
}
}
else {
DestoryA(pa);
return false;
}
}
else{
return false;
}
return true;
}
一种处理这种问题的方法是每次都检查指针是否为空,这样就没有很多嵌套了。
bool Init2(){
A* pa = nullptr;
B* pb = nullptr;
bool status = true;
pa = CreateA();
if (status && pa){
status = doStuffByA(pa);
}
if (status && pa){
pb = CreateBbyA(pa);
}
if (status && pa && pb){
status = doStuffByB(pb);
}
if (status && pa && pb){
return true;
}
else{
if (pb) { DestoryB(pb); };
if (pa) { DestoryA(pa); };
return false;
}
}
在C语言的代码里面,一种常用的技巧是使用goto
语句,一旦出错,就跳到错误处理的部分。
/* very common in C */
bool Init3(){
A* pa = nullptr;
B* pb = nullptr;
if (!(pa = CreateA())){
goto ErrExit;
}
if (!doStuffByA(pa)){
goto ErrExit;
}
if (!(pb = CreateBbyA(pa))){
goto ErrExit;
}
if (!doStuffByB(pb)){
goto ErrExit;
}
return true;
ErrExit:
if (pb) { DestoryB(pb); };
if (pa) { DestoryA(pa); };
return false;
}
在C++里面,使用goto
是不好的。一是,局部对象的初始化会被跳过,二是,如果还使用了异常,那么程序的控制逻辑就更加复杂了。所以在C++下,可以使用std::unique_ptr
来处理这个问题。
bool Init4()
{
auto deleterA = [&](A* p){ if (p) DestoryA(p); };
std::unique_ptr<A, decltype(deleterA)> pa(CreateA(),deleterA);
if (!pa){
return false;
}
if (doStuffByA(pa.get())){
return false;
}
auto deleterB = [&](B* p){ if (p) DestoryB(p); };
std::unique_ptr<B, decltype(deleterB)> pb(CreateBbyA(pa.get()), deleterB);
if (!pb){
return false;
}
if (doStuffByB(pb.get())){
return false;
}
pa.release();
pb.release();
return true;
}
如果出错了,就直接return false
,因为对象A和对象B都被智能指针封装了,在std::unique_ptr
析构的时候时候,对应的Destory
也就被调用了。这里需要注意的事情是:
- 因为
pb
定义的比pa
晚,所以pb
的释放会比pa
早;在这里是合理的,但是如果pa
和pb
被复制到其他的std::unique_ptr
,那么就不好保证其释放顺序了。所以要加以小心使用。 - 如果对象在函数结束以后还需要,那么记得
release()
智能指针,并且在其他地方保存原始指针。
网友评论