1

私のコードでは、HANDLEからsを使用していますwindows.h。彼らはのように使用されます

HANDLE h;
if (!openHandleToSomething(arg1, arg2, &h)) {
    throw std::exception("openHandleToSomething error");
}
/* Use the handle in other functions which can throw as well */
if (!CloseHandle(h)) {
    throw std::exception("closeHandle error");
}

CloseHandleご覧のとおり、取得と解放の途中で発生する可能性のあるすべての例外にこれを挿入する必要があります。したがって、1つを忘れて(または知らなかった派手なSEH例外があり)、ボイラーになる可能性があります。メモリリークが発生します。

CloseHandle最近、私はそのような場合の頭痛を取り除き、これを自動的に呼び出すべきであるRAIIについて読みました。また、C ++のようstd::auto_ptr<someType>に、で割り当てられたリソースの問題を解決するものがあることもわかりましたnew

しかし、私は使用せず、newただHANDLEtypedefあるためvoid *、をどのように使用すればよいのでしょうかstd::auto_ptr<someType>。どういうわけか、それにカスタム削除関数(if (!CloseHandle(h)) { throw std::exception("closeHandle error"); })を与えることが可能であるはずです。クラスのインスタンスがスコープ外になるたびにデストラクタが呼び出されるため、クラスの作成は別のメソッドになります。ただし、単純なことすべてにクラスを設けるのはやり過ぎです。

これらの偶発的なメモリリークを修正するにはどうすればよいですか?

ライブラリがなく、依存関係が大きい純粋なC ++のソリューションを好むことに注意してください。ただし、それらが本当に小さく、ほとんどの環境で使用されている場合を除きます。

4

6 に答える 6

6

頭に浮かぶアイデアの 1 つは、カスタムの deleterでboost::shared_ptrを使用することです。

于 2009-10-12T18:43:44.190 に答える
3

独自の単純な RAII イディオムを実装できます。

class auto_handle {
public:
    auto_handle() : handle_() {}
    ~auto_handle() {
        if (!CloseHandle(handle_)) {
            // Don't throw here (1), manage the error in other way.
        }
    }
    HANDLE& handle() { return handle_; }
private:
    auto_handle(const auto_handle&);
    auto_handle& operator=(const auto_handle&);
    HANDLE handle_;
};

(1)デストラクタからスローしないでください

auto_handle h;
if (!openHandleToSomething(arg1, arg2, &h.handle())) {
    throw exception("openHandleToSomething error"); // Now it is safe
}
于 2009-10-12T19:33:43.603 に答える
2

1) 使用しないでくださいauto_ptr<>。真剣に。これらの頭痛の種は必要ありません。使い慣れたコピー セマンティクスがないため、簡単に間違えてしまいます。

HANDLE2)基になるハンドルを提供するアクセサを提供する単純なオブジェクトでラップします。HANDLEこれは、を API 呼び出しに渡すために必要です。(暗黙的な変換よりもアクセサーの方が好ましいと思います。)

3)私は実際にラッピングHANDLEを気にしたことがないので、驚くべき落とし穴があるかどうかはわかりません. あるとしても指摘できない。私は何も期待しません-それは不透明な値です。しかし、それでは、誰が驚くべき落とし穴を期待していますか? 結局のところ、彼らは驚きです。

4) (もちろん) 適切な dtor を実装します。

于 2009-10-12T18:41:19.513 に答える
1

関数に渡すときにハンドルを提供する単純なラッパーが必要なだけです。

#include <stdexcept>
class HWrapper
{
    HANDLE h;
    bool   closed;

    public:
        HWrapper(A1 arg1,A2 arg2)
            :closed(false)
        {
            if (!openHandleToSomething(arg1, arg2, &h))
            {    throw std::runtime_error("openHandleToSomething error");
            }
        }
        ~HWrapper()
        {
            try
            {
                if (!closed)
                {   close();
                }
            }
            catch(...) {/*Exceptions should never leave a destructor */ }
            // Though you may want to log somthing.
        }
        void close()
        {
            closed = true;
            // Close can throw an exception.
            if (!CloseHandle(h))
            {    throw std::runtime_error("closeHandle error");
            }
        }

        /*
         * This allows you to just pass it to a function that takes an HANDLE
         * See the function:   functionThatUsesHandleButMayThrow();
         */
        operator HANDLE()
        {
            return h;
        }
    private:
    /*
     * For your use case there is not need to copy.
     * So explicitly disallow copying.
     *
     * Just pass the HWrapper object to any function that requires a handle.
     * The built in cast operator will convert it back to a Handle to be used
     * within these functions. While this object just retains ownership and
     * responcability for deleting the object when you are finished.
     *
     * This allows simple backwards compatibility with existing code.
     */ 
    HWrapper(HWrapper const& copy);            // Don't implement
    HWrapper& operator=(HWrapper const& copy); // Don't implement


};

void functionThatUsesHandleButMayThrow(HANDLE h)
{
}



int main()
{

    try
    {
        HWrapper   w(A1,A2);

        functionThatUsesHandleButMayThrow(w);
        /*
         * If you do not care about close throwing an excepion.
         * Then jsut let it fall out of scope. The destructor
         * will try and clean up. But if it fails it will drop the
         * exception.
         *
         * This is required because if another exception is propogating
         * throwing an exception terminates the application.
         */
    }
    catch(std::exception const& e)
    {
        std::cout << "Exception: " << e.what() << "\n";
    }


    try
    {

        HWrapper   w2(A1,A2);

        functionThatUsesHandleButMayThrow(w2);
        /*
         * If you do care abou the exception
         * The call close() manually. The exception will be thrown.
         *
         * But if an exception is already been thrown in
         * functionThatUsesHandleButMayThrow() then we will try and close it
         * in the destructor and not throw another exception.
         */
        w2.close();
    }
    catch(std::exception const& e)
    {
        std::cout << "Exception: " << e.what() << "\n";
    }
}
于 2009-10-12T19:29:11.413 に答える
1

std::auto_ptrこの状況には適していません。それには用途がありますが、これはその用途の 1 つではありません。Greg D によって提起された点を (ある程度) 修正するために、問題はポインターauto_ptrのセマンティクスの欠如ではなく、かなり奇妙な所有権のセマンティクスです。所有権のセマンティクスを割り当てると、ポインターのコピーが取得されません。、代わりにポインタの転送(つまり、担当者がリソースの新しい唯一の所有者になり、担当者は何も持っていません)。

ただし、ハンドルをクラスでラップする必要があります。私はこれを何度も行ってきましたが、非常にうまく機能しています。これを行ったときに特に驚くべきことには遭遇しませんでしたが、それは必ずしも多くのことを意味するわけではありません.ハンドルはWindowsで多くのことに使用されており、ハンドルのいくつかは簡単に奇妙なものを持っている可能性があります.

于 2009-10-12T19:00:55.823 に答える