5

質問を正確に表現する方法がわからなかったので、次に例を示します。

この関数プロトタイプを考えると:

void Foo(myClass* bar);

この使用を防ぎたい:

Foo(new myClass());

代わりに、以前に作成したオブジェクトが必要です。

myClass* bar = NULL;
bar = new myClass();
Foo(bar);

また

myClass bar;
Foo(&bar);

ありがとう。


編集

明確な例を次に示します。


void Mouse::SetImage(BITMAP* image, int focusX, int focusY) {
    if(_image) {
        set_mouse_sprite(NULL);
        set_mouse_sprite_focus(0, 0);
        show_mouse(NULL);
        destroy_bitmap(_image);
        _image = NULL;
    }
    if(image) {
        _image = create_bitmap(image->w, image->h);
        clear_bitmap(_image);
        blit(image, _image, 0, 0, 0, 0, image->w, image->h);
    }
    if(image == NULL) {
        focusX = 0;
        focusY = 0;
    }
    _focusX = focusX;
    _focusY = focusY;
    _dirtyImage = true;
}

ユーザーが渡す画像はすべて、オブジェクトの画像にコピーされます。

渡された画像をコピーした後で割り当てを解除し、その画像をプログラムの他の場所で使用すると、アクセス違反でプログラムがクラッシュします。

ストレージをインラインで割り当て、割り当てを解除しないと、メモリリークが発生します。実行中のプログラムの過程でSetImageメソッドを複数回呼び出すと、問題はさらに複雑になります。

代替ライブラリの使用やAllegroライブラリ自体に関するコメントは無視されますが、それが恐ろしいことはすでにわかっています。仕方がない。

4

5 に答える 5

19

あなたのデザインは選択をする必要があります。所有権を取得して削除するか、所有権を取得しないでください。いずれにせよ、関数の使い方を知るのはユーザー次第です。彼らはあなたの関数が画像を破壊することを知っている必要があります(そしておそらく必要に応じて彼ら自身のコピーを渡す)か、彼らは彼ら自身のリソースを管理するのに十分賢い必要があります。

通常、所有権を削除するためだけに所有権を奪うことは望ましくありません。だから私は何も削除しません。誰かが渡した画像を削除する能力を失うほど愚かである場合、それはこの機能の問題ではありません。言い換えれば、マーフィーからの保護を試みる必要がありますが、マキャヴェリからの保護を忘れてください。

とはいえ、生のポインタの使用は悪いです!貧弱なC++コードは、手動のリソース管理とリソースの問題によって特徴づけられます。画像の周りにラッパーが必要です。これにより、デストラクタ内の画像が削除されます。そうすれば、例外がスローされた場合でも、リークすることはありません。reset()古い画像リソースを破棄して新しい画像リソースを取得するメソッドを提供します。

共有所有権が必要なようですので、参照カウントリソースラッパーが必要になります。その後、問題は解決されます。誰かが「インライン」割り当てを行うと、共有ポインターに入れられ、完了すると自動的に削除されます。(さらに良いのは、explicitコンストラクターを用意して、誰かがリソースを共有することを知っている必要があるようにすることです。)

これは、と呼ばれるスマートポインタで行われますshared_ptrBoostには1つ、TR1には1つ、C++0xには1つあります。カスタム削除(イメージを解放するもの)を指定するだけで、リソース管理について二度と心配する必要はありません。

これは、すべてのリソースで実行する必要があります。ここでの概念は、スコープバウンドリソース管理(SBRM)です。自動(スタック)変数の有効期間ルールを利用して、リソースが自動的に管理されること。元の名前ですが、醜い名前であるResource-Acquisition Is Initialization(RAII)としても知られています。この領域を調査すると、コードがより簡単でクリーンになっていることがわかります。


パラメータのタイプを変更せずに実行することはできません。次のように変更できます。

void Foo(myClass*& bar);

非定数参照は左辺値にのみバインドできるため、次のようになります。

void foo(int*&);

int main(void)
{
    int *i = 0;
    int j;

    foo(i); // well-formed
    foo(&j); // ill-formed
    foo(new int); // ill-formed
}

ただし、これにより、左辺値のアドレスを取得することはできません。もちろん、簡単に行うことができます。

int main(void)
{
    int j;
    int* pj = &j;
    foo(pj); // well-formed
}

そしてそれは動作します。しかし、なぜあなたがこれをやりたいのか分かりません。


上記の解決策では、引数を変更できます(参照であるため)。関数内でconstを適用したい場合は、次のようなユーティリティを作成できます。

template <typename T>
class require_lvalue
{
public:
    require_lvalue(T& pX) :
    mX(pX)
    {}

    const T& get(void) const
    {
        return mX;
    }

    operator const T&(void) const
    {
        return get();
    }

private:
    // non-copy-assignable
    require_lvalue& operator=(const require_lvalue&);

    const T& mX;
};

void foo(require_lvalue<int*>);

関数内にconst-referenceがあることを除いて、同じ結果です。


MSVCにはバグがあり、これを受け入れることに注意してください。

foo(new int);

どちらの場合も、そうすべきではありませんが。(ただし、受け付けませんnew int()。)

于 2010-08-15T21:02:37.207 に答える
4

このような使用法の区別は不可能です。そして、すべての場合において、それは有効なパラメータです。なぜこれが必要なのか本当に理解できません...

于 2010-08-15T20:42:52.863 に答える
2

したがって、ポインタは使用しないでください...(左辺値)参照を使用してください:

void Foo(myClass& bar);
于 2010-08-15T21:01:48.710 に答える
2

これはあなたの仕事を解決しませんか?しかし、とにかくそのような場合にはstd::auto_ptrのようなものをお勧めします。

#include <iostream>

void test(int *& ptr)
{
    std::cout << *ptr << std::endl;
}

int main()
{
/* NEXT LINE WILL FAIL */
//  test(new int(5));

    int *b = new int(5);
    test(b);
    delete b;

    return 0;
}
于 2010-08-15T21:16:25.843 に答える
-1

CまたはC++では、関数のパラメーターに含まれる、メモリーが割り当てられた場所を決定する余裕はありません。より良い安全性が必要な場合は、.NETでプログラムしてください。

安全性を高めたい場合は、関数のシグネチャを完全に変更して自動ポインタを受け入れるだけではありません。そうすれば、セマンティクスが明確になり、誰が、または何がメモリを所有しているかについて混乱が生じることはありません。

于 2010-08-16T02:42:30.790 に答える