2

次のテストコードは、私がはるかに大きなアプリケーションで抱えている問題を示しています。このアプリケーションには、すべて1つの基本クラスから派生した複数のサーバーを「提供」するサービスがあります。次に、createInstanceを使用して、サーバーの種類に基づいて特定のサーバーへの「アクセス」を取得します(以下では「n」を使用)。次に、dynamic_castを使用して適切なサーバーとしてキャストします。これはすべて正常に機能します。

問題は、deleteInstanceを使用してサービスに戻り、サービスを削除して、内部サーバー関連のデータをクリーンアップしようとした場合です。私は良い通過メカニズムを見つけることができないようです、あるいはそれが私がしていることを達成するための有効な方法でさえあるかどうか。

#include <iostream>
#include <string>

class MM
{
public:
    virtual ~MM() {}

    virtual void start() = 0;
};

class M1 : public MM
{
public:
    void start()
    {
        std::cout << "M1 start" << std::endl;
    }
};

class M2 : public MM
{
public:
    void start()
    {
        std::cout << "M2 start" << std::endl;
    }
    void start( const std::string strName )
    {
        std::cout << "M2 start - " << strName << std::endl;
    }
};

MM * createInstance( int n )
{
    if( 2 == n )
    {
        return new M2;
    }
    else
    {
        return new M1;
    }
}

void deleteInstance( MM * & pInstance )
{
    delete pInstance;
    pInstance = NULL;
}

void deleteInstance2( MM ** ppInstance )
{
    delete *ppInstance;
    *ppInstance = NULL;
}

int main( int argc, char *argv[] )
{
    M1 *pM1 = dynamic_cast<M1 *>( createInstance( 1 ) );
    M2 *pM2 = dynamic_cast<M2 *>( createInstance( 2 ) );

    pM1->start();

    pM2->start();
    pM2->start( "test" );

    deleteInstance( pM1 );
    deleteInstance( pM2 );
    //deleteInstance2( &pM1 );
    //deleteInstance2( &pM2 );

    return 0;
}

情報を完成させるために、deleteInstanceの実装で受け取ったエラー:

68:25: error: invalid initialization of reference of type ‘MM*&’ from expression of type ‘M1*’
46:6: error: in passing argument 1 of ‘void deleteInstance(MM*&)’
69:25: error: invalid initialization of reference of type ‘MM*&’ from expression of type ‘M2*’
46:6: error: in passing argument 1 of ‘void deleteInstance(MM*&)’

およびdeleteInstance2の場合:

70:27: error: invalid conversion from ‘M1**’ to ‘MM**’
70:27: error:   initializing argument 1 of ‘void deleteInstance2(MM**)’
71:27: error: invalid conversion from ‘M2**’ to ‘MM**’
71:27: error:   initializing argument 1 of ‘void deleteInstance2(MM**)’
4

4 に答える 4

2

問題は、基本型へのポインターへの参照を使用して派生型へのポインターをバインドすると、型システムが壊れることです。次の動機付けの例を考えてみましょう。

void resetPtr( base*& b ) {
   static base instance;
   b = &instance;
}
int main() {
   derived *d;
   resetPtr( d );        // Now d points to a base, not a derived object!!!!
}

他の回答が指摘しているように(たとえば、適切な型を推測するテンプレートを使用するなどして)これを回避できますが、再設計してポインターを値で渡すことをお勧めします。

削除後にポインターを NULL にリセットするのが悪い考えなのはなぜですか?

ポインターを NULL にリセットすることの問題は、実際には問題が解決されず、独自の問題が追加されることです。

アプリケーションでポインターが有効かどうかを知るという問題は解決しません。一般に、特定のオブジェクトへのポインターを複数持つことができ、そのうちの 1 つだけを削除すると、ポインターの 1 つだけがリセットされるためです。 NULL にすると、(少なくともほとんどの場合) 最初と同じ状況になります。

アプリケーションのロジックのバグを隠すのに役立ちます。ポインターを NULL にリセットした後、NULL ポインターdeleteに対して安全であるため、ポインターを 2 回使用することによるアプリケーションの潜在的な問題は隠されdeleteます。これは良い考えだと思うかもしれませんが、最終的にはアプリケーションのクラッシュを回避できますが、長期的には悪い考えです。コアの問題がまだ残っているためです。つまり、設計が適切な所有権のセマンティクスを提供していないということです。

于 2012-05-19T02:21:05.933 に答える
1

この問題は、基本クラスと派生クラスのポインターとは何の関係もありません。問題は、MM へのポインターへのポインターを引数として受け入れるようにメソッドを宣言し、MM へのポインターだけを渡していることです。

参照によって MM へのポインターを渡すことができます。つまり、

void deleteInstance( T* &pInstance ) ...
于 2012-05-19T01:19:26.670 に答える
0

あなたがやろうとしていることを気に入るかどうかはわかりませんが、その理由を特定することはできません。アイデアは素晴らしいと思います。..しかし、あなたがそれを実装できる方法があります。

template<typename T>
void deleteInstance( T * & pInstance )
{
  // This conversion is here so you get a nice error if 
  // you try to use it on a type that isn't derived from MM.
  MM* tmp = pInstance;
  delete tmp;
  pInstance = NULL;
}
于 2012-05-19T01:14:51.197 に答える
0

が機能しない理由deleteInstanceは、M1* または M2* から MM* への変換によって作成された一時オブジェクトへの非 const 参照を取得しているためです。

が機能しない理由deleteInstance2は、Derived** が Base** に変換できないためです。このよくある質問はそれを非常によく説明しています。

于 2012-05-19T01:18:26.417 に答える