Effective C ++ Item 29: The realization (as "abnormal security" but worth the effort)

First, the presentation may throw an exception of cases

  • Here is a class, used to represent entrained background picture single GUI menu, which has a mutually exclusive as concurrency control:
void lock(Mutex* pm);   //锁定pm所指的互斥器
void unlock(Mutex* pm); //将互斥器解锁

class PrettyMenu {
public:
    void changeBackground(std::istream& imgSrc) {
        lock(&mutex);   //取得互斥器
        delete bgImage; //删除旧图片
        ++imageChanges; //修改图像更改次数
        bgImage = new Image(imgSrc); //安装新的背景图片
        unlock(&mutex); //释放互斥器
    }
private:
    Mutex mutex;     //互斥器
    Image* bgImage;  //目前使用的背景图片
    int imageChanges;//图片被修改的次数
};
  • The above changeBackground () member function is not "abnormal safe." Because exception safety function should have the following two characteristics:
    • ① not disclose any resource: the above code if the new Image () operation causes an exception, then never call unlock, then the mutex will be locked forever. So the member function does not guarantee that
    • ② does not allow data destruction: If new Image () operation causes an exception, then bgImage has been deleted, and the number imageChanges also be accumulated, so resources are changed. However, this function does not guarantee that

Second, problem-solving resource leaks

  • This problem is easily solved, discusses how objects to manage resources, Clause 14 also introduced their own design named Lock class to manage the mutex is defined as follows in Article 13:
//初始化对象时锁住互斥器,对象释放时自动解除互斥器
class Lock
{
public:
    //获得资源
    explicit Lock(Mutex* pm) :mutexPtr(pm) {
        lock(mutexPtr);
    }
 
    //释放资源
    ~Lock() { unlock(mutexPtr); }
private:
    Mutex *mutexPtr;
};

//重写修改PrettyMenu的成员函数
void PrettyMenu::changeBackground(std::istream& imgSrc) {
    Lock ml(&mutex);//将互斥器封装在类中进行管理
    delete bgImage; //删除旧图片
    ++imageChanges; //修改图像更改次数
    bgImage = new Image(imgSrc); //安装新的背景图片
}
  • The above will be encapsulated in a mutex object class, initialization mutex is locked, when the end object lifecycle we do not need to manually release the mutex, a destructor will automatically help us mutex is released, so that methods can solve the problem of resource leak

Third, to solve the problem of data corruption

  • The above describes a general solution to resource leaks, and now to look at the problem of data destruction. Prior to this we first define some terms:
    • ① basic promise: If an exception is thrown, everything in the program should remain in the active state
    • ② strong guarantee: if the program throws an exception, program state should not be guaranteed. Call such a function should guarantee: If the function is successfully completed successfully; if the function fails, the program will return to the "before calling the function" of the state
    • ③ not throw guarantee: commitment not to throw an exception, because they are always able to fulfill the functions of their original commitment
  • Exception-safe code must be provided to ensure that one of the three, if not, then the code is not exception safe
  • For changeBackground () function, in order to ensure that the data is not destroyed, it can be changed to the following code:
    • Using smart pointers to manage the Image object
    • Reordering changeBackground () statement in the function of the order only after accumulating imageChanges, such that the replacement image
    • In changeBackground () internal function need to manually delete delete old images of , as it has been managed by the smart pointer automatically deleted (is called the internal reset)
class PrettyMenu {
//...
private:
    std::tr1::shared_ptr<Image> bgImage;
};

//重写修改PrettyMenu的成员函数
void PrettyMenu::changeBackground(std::istream& imgSrc) {
	Lock ml(&mutex);
	bgImage.reset(new Image(imgSrc));
	++imageChanges;
}

Four, copy-and-swap strategy

  • "Three" solves the problem of data corruption, but there is a problem is not resolved, it is in the changeBackground () function, the constructor may throw an exception Image
  • principles and swap strategies copy is: do you intend to copy the object (the original) to modify and make changes in the body copy:
    • If a copy of the modified body throws an exception, then the original object is not to change state
    • If you modify a copy of the body without throwing an exception, then it will be modified copy of the original object replacement (swap)
  • pimpl idiom way:
    • Original object is encapsulated into a class (or structure) of
    • This approach is called pimpl idiom, in terms of 31 will explain
  • For the above PrettyMenu, a typical written as follows:

Case presentation

  • For the above PrettyMenu, a typical written as follows: 
//将bgImage和imageChanges从PrettyMenu独立出来,封装到一个结构中
struct PMImpl {
    std::tr1::shared_ptr<Image> bgImage;
    int imageChanges
};

class PrettyMenu {
    //...
private:
    std::tr1::shared_ptr<PMImpl> pImpl; //创建一个该结构
};
  • Now we are revising changeBackground () function:
void PrettyMenu::changeBackground(std::istream& imgSrc) {
    using std::swap; //见条款25

    Lock ml(&mutex);
	
    //以pImpl为原件,创建一个副本,然后在副本上做修改
    std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl));
    pNew->bgImage.reset(new Image(imgSrc));
    pNew->imageChanges++;

    //如果上面副本的修改没有抛出异常,那么交换副本与原件
    swap(pImpl, pNew);
}

V. Summary

  • Exception safety function exception does not allow any data leak resources or even structural damage occurs. Such a function is divided into three possible guarantees: basic, strong type, nothrow type
  • "Strong assurance" are often able to achieve up to copy-and-swap, but the "strong guarantee" does not have practical significance for all functions can be realized or
  • The weakest function provides "abnormal security assurance" is usually equal to the maximum value of the various functions called "abnormal security guarantees" in the
Released 1494 original articles · won praise 1053 · Views 420,000 +

Guess you like

Origin blog.csdn.net/qq_41453285/article/details/104475116