C++Primer学習ノート_89_大型プログラム用ツール-エラー処理[続き2]

5921 ワード

大型プログラム用のツール
--異常処理[続き2]
八、自動資源解放
次の関数を考慮します.
void f()
{
    vector<string> v;
    string s;

    while (cin >> s)
    {
        v.push_back(s);
    }

    string *p = new string[v.size()];
    //...
    delete p;
}

通常、配列とvectorは関数を終了する前に取り消され、関数の最後の文は配列を解放し、関数の終了時にvectorを自動的に取り消す.
ただし、関数内部に異常が発生した場合、vectorは配列を解放することなく取り消されます.問題は配列が自動的に解放されないことです.newの後にdeleteの前に発生した異常により、配列は取り消されなかった.異常が発生するたびにvector構造関数の実行が保証されます.
クラスによるリソース割り当ての管理
構造関数の実行は重要なプログラミング技術の出現をもたらした:異常に安全である---異常が発生してもプログラムは正常に動作することができる.この場合、「セキュリティ」は、「例外が発生した場合、割り当てられたリソースが適切に解放される」ことを保証することから来ます.
クラスを定義してリソースの割り当てと解放をカプセル化することで、リソースの正確な解放を保証します.「リソースの割り当ては初期化」(RAID)
具体的には、コンストラクタがリソースを割り当て、コンストラクタがリソースを解放するようにリソース管理クラスを設計します.リソースを割り当てる場合は、そのクラスタイプのオブジェクトを定義します.異常が発生しない場合は、取得したリソースのオブジェクトが役割ドメインを超えたときにリソースを解放します.さらに重要なのは、オブジェクトが作成された後に、オブジェクトが役割ドメインを超える前に異常が発生した場合、コンパイラは、オブジェクトを展開定義オブジェクトの役割ドメインの一部として取り消すことを保証します.次のようになります.
class Resource
{
public:
    Resource(parms p):r(allocate(p)) {}
    ~Resource()
    {
        release(r);
    }

private:
    resource_type *r;
    resource_type *allocate(parms p);

    void release(resource_type *);
};

resourceクラスは、リソースを割り当て、回収するタイプで、リソースを表すデータ・メンバーを保存します.Resourceのコンストラクション関数はリソースを割り当て、コンストラクション関数はそれを解放します.
void fcn()
{
    Resource res(args); //    
    //...

}//      

関数が正常に終了すると、Resourceオブジェクトが役割ドメインを超えたときにリソースが解放されます.関数が異常で早期に終了すると、コンパイラは例外処理プロセスの一部としてResourceの構造関数を実行します.
【ベストプラクティス】
例外がある可能性のあるプログラムおよびリソースを割り当てるプログラムは、クラスを使用してそれらのリソースを管理する必要があります.リソース管理クラスを使用して、例外が発生した場合にリソースを解放することを保証します.
//P591   17.7
//1
void exercise(int *b,int *e)
{
    vector<int> v(b,e);
    int *p = new int[v.size()];

    try
    {
        ifstream in("ints");
        //...
    }
    catch(...)
    {
        delete p;
        //...
    }
}
//2
template <typename resource_type>
class Resource
{
public:
    Resource(size_t sz):r(new resource_type[sz]) {}
    ~Resource()
    {
        release(r);
    }

private:
    resource_type *r;

    void release(resource_type *);
};

void exercise(int *b,int *e)
{
    vector<int> v(b,e);
    Resource<int> res(v.size());
    //...
    ifstream in("ints");
    //...
}

九、auto_ptrクラス
標準ライブラリauto_ptrクラスは、前節で説明した異常に安全な「リソース割り当てすなわち初期化」の技術例です.auto_ptrクラスは、動的に割り当てられたオブジェクトに異常なセキュリティを提供するタイプのパラメータを受け入れるテンプレートです.auto_ptrクラスはヘッダファイルmemoryで定義されます.
auto_ptrクラス
auto_ptrap;
apというバインドされていないauto_を作成ptrオブジェクト
auto_ptrap(p);
apという名前のauto_を作成ptrオブジェクト、apはポインタpが指すオブジェクトを有する.このコンストラクション関数はexplicitです.
auto_ptrap1(ap2);
ap 1という名前のauto_を作成ptrオブジェクト、ap 1はap 2に格納されていたポインタを保存する.所有権をap 1に渡し、ap 2はバインドされていないauto_になります.ptrオブジェクト
ap1= ap2
所有権ap 2をap 1に転送します.ap 1が指すオブジェクトを削除し、ap 1がap 2が指すオブジェクトを指し、ap 2をバインドされていない
~ap
構造関数apが指すオブジェクトを削除
*ap
apにバインドされたオブジェクトへの参照を返します.
ap->
apが保存したポインタを返します
ap.reset(p)
pとapの値が異なる場合、apが指すオブジェクトを削除し、apをpにバインドします.
ap.release()
apが保存したポインタを返し、apをバインドされていない
ap.get()
apが保存したポインタを返します
【地雷に注意】
auto_ptrはnewから返されるオブジェクトを管理するためにのみ使用でき、動的に割り当てられた配列を管理することはできません(定義されていないランタイム動作を引き起こす可能性があります).
auto_ptrがコピーまたはコピーされる場合、異常な動作があるためauto_ptrは標準ライブラリコンテナタイプに格納されます.
各auto_ptrオブジェクトは、オブジェクトにバインドされるか、オブジェクトを指します.auto_ptrオブジェクトが1つのオブジェクトを指す場合、そのオブジェクトは「所有」と言える.auto_ptrオブジェクトが役割ドメインを超えたり、別途取り消されたりした場合、自動的にauto_を回収します.ptrが指す動的割り当てオブジェクト.
class Text
{
public:
    Text()
    {
        cout << "Text" << endl;
    }
    ~Text()
    {
        cout << "~Text" << endl;
    }
};

int main()
{
    //  !
    auto_ptr<Text> ap(new Text);
    Text *p = new Text;
}

1、異常安全なメモリの割り当てにauto_を使用するptr
通常のポインタでメモリを割り当て、deleteを実行する前に異常が発生した場合、メモリは自動的に解放されません.
void f()
{
    int *ip = new int(42);
    //         ,        ,       
    delete ip;
}

Auto_を使用する場合ptrオブジェクトを置き換えると、このブロックを早期に終了してもメモリが自動的に解放されます.
void f()
{
    auto_ptr<Text> ap(new Text);
    throw runtime_error("TEXT");
}

この例では、コンパイラは、fを超える展開前にapの構造関数を実行することを保証する.
2、auto_ptrは任意のタイプのポインタに保存できるテンプレートです
auto_ptrクラスは、単一のタイプのパラメータを受け入れることができるテンプレートです.
    auto_ptr<string> strPtr(new string("Brontosaurus"));
    cout << *strPtr << endl;

3、auto_ptrポインタにバインド
最も一般的な場合はauto_ptrオブジェクトはnew式で返されるオブジェクトのアドレスに初期化されます.
    auto_ptr<int> intPtr(new int(1024));

ポインタを受け入れるコンストラクション関数はexplicitコンストラクション関数なので、auto_を作成するには初期化の直接形式を使用する必要があります.ptrオブジェクト:
    auto_ptr<int> iP = new int(1024);   //Error
    auto_ptr<int> pI(new int(1024));    //OK

iPが指すnew式で作成されたオブジェクトは、役割ドメインを超えたときに自動的に削除されます.iPがローカルオブジェクトである場合、iPが指すオブジェクトはpiを定義するブロックの最後に削除されます.異常が発生した場合、iPも役割ドメインを超え、解析関数は自動的にiPを実行する解析関数を異常処理の一部とする.iPがグローバルオブジェクトである場合、プログラムの最後にiPリファレンスのオブジェクトを削除します.
4、auto_を使うptrオブジェクト
auto_ptrクラス定義は、auto_ptrはこれらのオペレータを定義するので、auto_を内蔵ポインタと同様に使用できます.ptrオブジェクト:
    auto_ptr<string> strPtr(new string("HELLO!"));
    cout << *strPtr << endl;

    *strPtr = "TRex";
    string s = *strPtr;
    cout << s << endl;

    if (strPtr -> empty())
    {
        cout << "Empty!" << endl;
    }
    else
    {
        cout << "Not Empty!" << endl;
    }

auto_ptrの主な目的はauto_の自動削除を保証することです.ptrオブジェクトが参照するオブジェクトは、通常のポインタ動作をサポートします.ご覧のように、オブジェクトを自動的に削除することで、アドレス値のコピーとアクセス方法についてauto_ptrsは通常のポインタとは著しく異なる.