4

さまざまなクラス タイプのオブジェクトを含むアプリケーションがあります。オブジェクトはポインターによって参照されます。null ポインターは、関連付けられたオブジェクトが存在しないことを示します。現在、オブジェクトへのポインターを使用するたびに、ポインター値が null かどうかをテストし、null である適切なアクションを実行するため、呼び出しコードは面倒です。存在しない場合に実行されるデフォルトのアクションはオブジェクトのタイプに依存するため、呼び出しプログラムではなく、オブジェクト自体のクラスでエンコードすることをお勧めします。これにより、次のような構造が得られます。

class C
{ ... 
  void member_func() //non-virtual !
  { if (this) { do something with the object ... }
    else { take some default action }
  }
  ...
};

オブジェクトが存在しない場合はルックアップ テーブルが存在せず、仮想呼び出しが失敗するため、明らかにメンバー関数を仮想にすることはできません。しかし、このコードは非仮想メンバー関数に対して正当な C++ ですか? 私が試したコンパイラでは正しく動作しているようですが、移植性がない可能性が心配です。標準では、そのような構造を明示的に許可または明示的に禁止する条項を見つけることができません。

4

5 に答える 5

12

thisメンバー関数では null になることはないため、実行するチェックは役に立ちません。

Matthieu M. がコメントで指摘したように、コードで次のようなことを行うと:

C* c = 0; 
c->member();

これは未定義の動作を引き起こし、それは何か悪いことです。

于 2013-01-08T14:59:11.023 に答える
6

指摘されているthisように、null ポインターになることはできません。そうであれば、未定義の動作をすでに呼び出しています。代わりに、次のようにオーバーロードされた関数のセットを作成できます。

void DoTheThing(C* cp)
{
    if (cp)
        cp->member_func();
    else
    {
        // take some default action
    }
}

void DoTheThing(B* bp)
{
    if (bp)
        bp->some_other_member_func();
    else
    {
        // take some default action
    }
}

呼び出したい関数が各クラスで同じ名前を持っている場合、そのクラスのデフォルト アクションを実行する各クラスで静的関数を作成し (すべて同じ名前で)、テンプレートを作成できます。

template<typname T>
void DoTheThing(T* tp)
{
    if (tp)
        tp->member_func();
    else
        T::default_action()
}
于 2013-01-08T15:19:05.627 に答える
1

標準的には、コードは合法ではありませんが、実際に使用されています(つまり、悪い習慣です)。

実際、IIRCMFCはこれらのチェックを内部で使用します。

于 2013-01-08T15:01:07.170 に答える
1

問題ないか確認this == NULLします。NULLオブジェクトポインタを介してメソッドを呼び出すことはです。

チェックをどこかに保持したい場合は、保持されているポインターがNULLの場合に適切なアクションを実行できるスマートポインタークラスにチェックを入れることができます。「適切なアクション」が保持されているタイプによって一意に決定される場合は、トレイトクラスを使用して指定できます。

このようにして、NULLチェックとそのロジックが一緒に保持され、呼び出し元またはメソッドコードのいずれにも混在しません。


// specialize this to provide behaviour per held type
template <typename T> struct MaybeNullDefaultAction {
    void null_call() { throw std::runtime_error("call through NULL pointer"); }
}

template <typename T> class MaybeNull: MaybeNullDefaultAction<T> {
    T *ptr;
public:
    explicit MaybeNull(T *p) : ptr(p) {}

    T* operator-> () {
        if (!ptr)
            null_call();
        // null_call should throw to avoid returning NULL here
        return ptr;
    }
};

残念ながら、私は投げずにこれを行う方法を見つけることができません。すべてのメソッド名の関数呼び出しをインターセプトする方法はありません。そうしないと、*thisから戻っoperator->てで作業を行うことになりoperator()ます。

于 2013-01-08T15:10:34.803 に答える
0

そんなことは許されないと思います。あなたは標準への参照を求めました。最初に興味深いのは 9.3.1 非静的メンバー関数 1.:

非静的メンバー関数は、クラス メンバー アクセス構文 (5.2.5、13.3.1.1) を使用して、そのクラス型のオブジェクト、またはそのクラス型から派生したクラス (第 10 節) のオブジェクトに対して呼び出すことができます。

次に、5.2.5 クラス メンバー アクセス、2. を見てみましょう。

式 E1->E2 は、同等の形式 (*(E1)).E2; に変換されます。5.2.5 の残りの部分では、最初のオプション (ドット) のみを扱います。

したがって、E1 が のnullptr場合、*E1 は許可されません。少なくとも私の推測はそうです。

于 2013-01-08T15:45:59.723 に答える