0

    class C {
    public
      T x;
    };

x のコンストラクターが、構築しているC のインスタンスを 暗黙のうちに知るエレガントな方法はありますか?


私は、いくつかの汚れた洗練されていない機械でそのような動作を実装しました。これは、sqlite3 ラッパーに必要です。私が見たすべてのラッパーが好きというわけではありません.API IMOは醜く不便です. 私はこのようなものが欲しい:


    class TestRecordset: public Recordset {
    public:
      // The order of fields declarations specifies column index of the field.
      // There is TestRecordset* pointer inside Field class, 
      // but it goes here indirectly  so I don't have to 
      // re-type all the fields in the constructor initializer list.
      Field<__int64> field1;
      Field<wstring> field2;
      Field<double> field3;

      // have TestRecordset* pointer too so only name of parameter is specified
      // in TestRecordset constructor
      Param<wstring> param;

      virtual string get_sql() {
        return "SELECT 1, '1', NULL FROM test_table WHERE param=:PARAM";
      }

      // try & unlock are there because of my dirty tricks.
      // I want to get rid of them.

      TestRecordset(wstring param_value)
      try : Recordset(open_database(L"test.db")), param("PARAM") {
        param = param_value;
       // I LOVE RAII but i cant use it here. 
       // Lock is set in Recordset constructor, 
       // not in TestRecordset constructor.
        unlock(this);
        fetch();
      } catch(...) {
        unlock(this);
        throw;
      }
    };

I want to clarify the fact - it is a part of the working code. You can do this in C++. I just want to do it in a more nice way.


I've found a way to get rid of unlock and try block. I've remembered there is such a thing as thread local storage. Now I can write constructor as simple as that:

  TestRecordset(wstring param_value): 
    Recordset(open_database(L"test.db")), param("PARAM") {
        param = param_value;
        fetch();
   }


to dribeas: My objective is to avoid redundant and tedious typing. Without some tricks behind the scene I will have to type for each Field and Param:

TestRecordset(wstring param_value): Recordset(open_database(L"test.db")), param(this, "PARAM"),
   field1(this, 0), field2(this, 1), field3(this, 2) { ... }

それは冗長で、醜く、不便です。たとえば、SELECT の途中で新しいフィールドを追加する必要がある場合は、すべての列番号を書き直す必要があります。あなたの投稿に関するいくつかのメモ:

  1. フィールドと Params、デフォルトのコンストラクターによって初期化されます。
  2. コンストラクター内の初期化子の順序は関係ありません。フィールドは常に宣言順に初期化されます。この事実を使用して、フィールドの列インデックスを追跡しました
  3. 基本クラスが最初に構築されます。したがって、Fields が作成されると、Recordset の内部フィールド リストは、Filed の既定のコンストラクターで使用できるようになります。
  4. ここではRAIIを使用できません。Recorset コンストラクターでロックを取得し、すべての Fields が構築された後に TestRecordset コンストラクターで強制的に解放する必要があります。
4

5 に答える 5

8

いいえ。オブジェクトは、機能するためにどこから使用されているかを知る必要はありません。x に関する限り、これは T のインスタンスです。それだけです。クラス C のメンバー、クラス D のメンバー、自動、一時などによって動作が異なることはありません。

さらに、T コンストラクターが C のインスタンスを認識していたとしても、そのメンバーが構築されていないため、もちろんまだ構築が完了していないため、C のそのインスタンスは不完全です。C++ では、自分自身を撃つ可能性がたくさんありますが、別のクラスのコンストラクターで不完全なオブジェクトへの参照を提供することは、その 1 つではありません。

あなたのコード例を近似するために私が考えることができる唯一のことは、次のようなことをすることです

#define INIT_FIELDS field1(this), field2(this), field3(this)

フィールドのリストの直後に、初期化子リストで INIT_FIELDS を使用し、#undef します。まだ重複していますが、少なくともすべてが 1 か所にあります。ただし、これはおそらく同僚を驚かせるでしょう。

フィールドを忘れないようにするもう 1 つの方法は、Field からゼロ引数コンストラクターを削除することです。繰り返しますが、入力を行う必要がありますが、少なくとも何かを忘れた場合は、コンパイラがそれをキャッチします。イニシャライザ リストの非 DRY な性質は、C++ が対応しなければならないものだと思います。

于 2008-12-03T18:46:52.493 に答える
2

One by One の回答に追加すると、実際に尋ねるべき質問は次のとおりです。

于 2008-12-03T18:50:12.690 に答える
1

私はそうは思わない。

純粋な好奇心から、なぜそれが問題になるのでしょうか? これが役立つコンテキストはありますか?

M.

于 2008-12-03T18:48:14.800 に答える
0

あなたのコードに興味があります。すべてのフィールドと param 属性には TestRecordSet に戻るポインタがあるが、初期化する必要はないとコメントしていますか? それとも、構築中に this ポインタを渡す必要がないようにするにはどうすればよいですか?

コンストラクターの初期化リストにすべてのフィールドを追加することを避けたい場合は、目的に欠陥があります。初期化リスト内のすべてのメンバーを常に初期化し、クラスで宣言されているのと同じ順序で初期化する必要があります (これは言語強制ではなく、よりグローバルに学習した経験です)。

try コンストラクターブロックの使用は、実際に必要な場合にのみ、その機能の使用を推奨しただけです (興味のある人はGOTW#66を読んでください)。RecordSet メンバーが構築された (したがってロックが取得された) 後、コンストラクターで何か問題が発生した場合 [以下の引用を参照] RecordSet は破棄され、内部で RAII を使用している場合はロックが解放されるため、 try/catch は実際には必要ないかもしれません。

C++03、15.2 例外処理 / コンストラクタとデストラクタ

部分的に構築または部分的に破棄されたオブジェクトは、完全に構築されたすべてのサブオブジェクト、つまり、コンストラクターの実行が完了し、デストラクタがまだ実行を開始していないサブオブジェクトに対して実行されるデストラクタを持ちます。

于 2008-12-03T20:36:32.910 に答える
0

私は常に C# でこのようなことを試しています。それにはリフレクションを使用しています。

C++ 用のリフレクションまたはコード生成ライブラリを入手して、やりたいことを実行できるようにすることを検討してください。

ここで、C++ 用の優れたリフレクションまたはコード生成ライブラリを見つける方法を説明することはできませんが、それは別の問題です。

于 2008-12-03T19:35:59.617 に答える