C++.Coding.Standards.1918.Rules.Guidelines [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

C++.Coding.Standards.1918.Rules.Guidelines [Electronic resources] - نسخه متنی

Herb Sutter, Andrei Alexandrescu

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید


Discussion


These are key functions that must not fail because they are necessary for the two key operations in transactional programming: to back out work if problems are encountered during processing, and to commit work if no problems occur. If there's no way to safely back out using no-fail operations, then no-fail rollback is impossible to implement. If there's no way to safely commit state changes using a no-fail operation (notably, but not limited to, swap), then no-fail commit is impossible to implement.C++03] §15.2(3)

No destructor operation defined in the C++ Standard Library [including the destructor of any type that is used to instantiate a standard library template]

will throw an exception.

[C++03] §17.4.4.8(3)

Destructors are special, and the compiler invokes them automatically in various contexts. If you write a classlet's call it

Nefarious whose destructor might fail (usually by throwing an exception; see Item 72), you incur the following consequences:

  • Nefarious

    objects are hard to use safely in normal functions:
    You can't reliably instantiate automatic

    Nefarious objects in a scope if that scope might be exited through an exception. If that happened,

    Nefarious 's destructor (automatically invoked) might attempt to throw an exception as well, which would result in sudden death of your entire program via

    std::terminate . (See also Item 75)

  • Classes with

    Nefarious

    members or bases are also hard to use safely:

    Nefarious ' poor behavior extends to any class of which

    Nefarious is a member or a base class.

  • You can't reliably create global or static

    Nefarious

    objects either:
    Any exception its destructor might throw can't be caught.

  • You can't reliably create arrays of

    Nefarious:
    In short, the behavior of arrays is undefined in the presence of destructors that throw because there is no reasonable rollback behavior that could ever be devised. (Just think: What code can the compiler generate for constructing an array of ten

    Nefarious objects where, if the fourth object's constructor throws, the code has to give up and in its cleanup mode tries to call the destructors of the already-constructed objects… and one or more of those destructors throws? There is no satisfactory answer.)

  • You can't use

    Nefarious

    objects in standard containers:
    You can't store

    Nefarious objects in standard containers or use them with any other part of the standard library. The standard library forbids all destructors used with it from throwing.


Deallocation functions, including specifically overloaded

operator delete and

operator delete[] , fall into the same category, because they too are used during cleanup in general, and during exception handling in particular, to back out of partial work that needs to be undone.

Besides destructors and deallocation functions, common error-safety techniques rely also on swap operations never failingin this case, not because they are used to implement a guaranteed rollback, but because they are used to implement a guaranteed commit. For example, here is an idiomatic implementation of

operator= for a type

T that performs copy construction followed by a call to a no-fail

Swap :

T& T::operator=( const T& other ) {
T temp( other );
Swap( temp );
}

(See also Item 56.)

Fortunately, when releasing a resource, the scope for failure is definitely smaller. If using exceptions as the error reporting mechanism, make sure such functions handle all exceptions and other errors that their internal processing might generate. (For exceptions, simply wrap everything sensitive that your destructor does in a

try /

catch(…) block.) This is particularly important because a destructor might be called in a crisis situation, such as failure to allocate a system resource (e.g., memory, files, locks, ports, windows, or other system objects).Item 75.)


/ 521