2

私は VS2008 でこのコードを使用しようとしました (そして、サンプルにあまりにも多くのコンテキストが含まれている可能性があります...):

class Base
{
    public:
    void Prepare() {
       Init();
       CreateSelectStatement();
       // then open a recordset
    }
    void GetNext() { /* retrieve next record */ }
    private:
    virtual void Init() = 0;
    virtual string CreateSelectStatement() const = 0;
};
class A : public Base
{
   public:
   int foo() { return 1; }
   private:
   virtual void Init() { /* init logic */ }
   virtual string CreateSelectStatement() { /* return well formed query */ }
};

template<typename T> class SomeValueReader : protected T
{
   public:
   void Prepare() { T::Prepare(); }
   void GetNext() { T::GetNext(); }
   T& Current() { return *this; } // <<<<<<<< this is where it is interesting
   SomeValue Value() { /* retrieve values from the join tables */ }
   private :
   string CreateSelectStatement() const
   {
   // special left join selection added to T statement
   }
};

void reader_accessAmemberfunctions_unittest(...)
{
   SomeValueReader<A> reader();
   reader.Prepare();
   reader.GetNext();
   A a = reader.Current();
   int fooresult = a.foo();
   // reader.foo()            >> ok, not allowed
   Assert::IsEqual<int>( 1, fooresult );
};

これは期待どおりに動作します。つまり、"A" メンバー関数にアクセスでき、fooresult は 1 を返します。ただし、unittest 関数の最後でオブジェクトが削除されると、例外がスローされます。

System.AccessViolationException: 保護されたメモリを読み書きしようとしました。これは多くの場合、他のメモリが破損していることを示しています

Current() 関数の戻り値の型を次のように変更すると:

T* Current()
{
   T* current = dynamic_cast<T*>(this);
   return current;
}

その後、すべて問題なく、単体テストはアクセス違反なしで終了します。最初の Current() 実装の何が問題だったのか誰か教えてもらえますか? ありがとう、ブーシェ。

4

3 に答える 3

5

実装された関数の値を返すように CreateSelectStatement を変更した後 (純粋な仮想ではありません)

string CreateSelectStatement() const { return ""; }

およびリーダーの宣言を変更します(宣言は、C ++の関数プロトタイプとして厳密に解釈する必要があります)

SomeValueReader<A> reader;

上記の例は、gcc を使用してエラーなしでコンパイルおよび実行されるため、実際のエラーは上記のソースに存在しない可能性があると思われます。残念ながら、現時点では VC でテストできません。

あなたが提案した変更が問題を解決する明確な理由はわかりません。私が見ることができる唯一の他のエラーは、Base に宣言された仮想デストラクタがないことです。クラスが実際の型ではない場合)、不適切なデストラクタが起動します。次のように宣言する必要があります

virtual ~Base() {}

たとえ空であっても。

テンプレートと仮想関数をこのように使用するのはスタイル的にも少し奇妙です。ここでは、テンプレートを使用してコンパイル時に関数を解決しているためです。SomeValueReader が (メンバー変数を持つのではなく) T から派生する必要がある理由もわかりません。

于 2009-12-11T16:47:27.760 に答える
1

わかりました、私の悪い私はVSからコードをコンパイルしようとしていません。私は単にそれをタイプし、意図的にいくつかの詳細を省略していました。サンプルを直接テストせず、実際のプロジェクトで見た動作のみに依存することは非常に悪い考えでした。したがって、コンパイル可能なバージョンは次のようになります。

/* /clr option enabled */

class Base
{
public:
   void FuncA() {}
protected :
   Base() {}
};

class Derived : public Base
{
public:
   int foo() { return 1; }
};

template<typename T> class SomeValueReader : protected T
{
public:
   void FuncA() { T::FuncA(); }
   T& Current() { return *this; }
};


void main(char* args)
{
   SomeValueReader<Derived> reader;
   reader.FuncA();
   Derived derived;
   derived = reader.Current();
   int fooresult = derived.foo();
   //reader.foo()            >> ok, not allowed
};

さて、アクセス違反を生成するためにこのサンプルを作成することはできないと言わざるを得ません。したがって、それは無関係です。それにもかかわらず、私が提案した修正は、私の実際のプロジェクトで私の問題に気付いた唯一の言葉であり、なぜだろうと思っていました。

項目34:継承よりも構成を
優先する私はこの一般的なガイドラインをよく知っており、SomeValueReaderを構成として定義したいと思いました。ただし、SomeValueReaderは、Tに適応できるように、保護された関数とTのメンバーにアクセスする必要があります。メンバーTだけを持つと、SomeValueReaderにその責任を実行するのに十分な情報が提供されません。さらに、SomeValueReaderは、一連のプライベート仮想関数を実装することにより、Baseパブリック非仮想インターフェイスを活用します。そうしないと、コードまたはロジックを複製する必要があります。だから、私はこれらのオプションで終わります、私はどちらかです:

  • SomeValueReaderをBaseのフレンドとして宣言し、いくつかのロジックを複製して、Tで保護されたメンバーにアクセスします。
  • Baseで保護されたメソッドを公開する(そして、あまりにも多くの情報をすべての人に公開する)。
  • または、この奇妙な保護された継承を試してください(ただし、複雑さが増します)。

私は別のオプションを逃したかもしれません。友達のクラスで「ごまかす」ことができなかったので、このテンプレートポリモーフィズムを使用することにしました。しかし、私は提案を受け入れています。

constとデストラクタ
がありませんconstは、不注意(およびコードのコンパイルを試みないこと)によるエラーでした。
行方不明のデストラクタは、重要な詳細とは見なされなかったため、省略されていました。しかし、それが間違いだったと考える人はほとんどいません。実際、これは私もそうするでしょう。メモリの破損により、デストラクタ内のエラーに到達することになります。しかし、実際のプロジェクトでは、ベースデストラクタは実際にはパブリックで仮想です。または、Base *が使用されていないため、この例では保護されており、非仮想である可能性があります。

于 2009-12-11T19:45:42.080 に答える
1

Visual Studio にはアクセスできませんが、コードの問題の 1 つは、クラス A で CreateSelectStatement() が const として宣言されていないことです。A をインスタンス化しようとしなければ問題ありません (つまり、Base と同じように純粋な仮想クラスです)。ただし、reader_accessAmemberfunctions_unittest でインスタンス化します。コンパイラがこれに対してエラーを生成することを期待していました.... g ++ 4.4.1はそうです。おそらくこれはあなたの問題ではありません。あなたのサンプルコードには他にもいくつかのバグが含まれているため、わかりにくいです。コンパイル可能でありながら、例を可能な限り単純に保つようにしてください (たとえば、インクルードするヘッダー ファイルが含まれている必要があります)。コードには余分なステートメントとコンパイルできない疑似コードが含まれているため、デバッグ作業がより困難になります。コードを最も単純な形式に縮小することから多くのことを学び、多くの場合、支援を求めることなく問題を解決できるようになります。これは、コードをデバッグするための基本的な手順です。

于 2009-12-11T17:03:08.147 に答える