4

クラスとをこの順序Bazで継承するクラスがあるとします。class のコンストラクターは、オブジェクトへのポインターを受け取ります。私がやりたいことは、オブジェクトとしてコンストラクターに渡すことです。FooBarBarFoothisFooBar

Baz () : Foo(), Bar(this) {}

実際の例:

#include <iostream>
class Bar;
class Foo {
  public:
  virtual ~Foo() {}
  virtual void parse_bar (Bar&) const = 0;
};  

class Bar {
  private:
  const Foo * parser;
  public:
  Bar (const Foo * parser_in) : parser(parser_in) {}
  virtual ~Bar() {}
  void parse_self () { parser->parse_bar (*this); }
};  

class Baz : public Foo, public Bar {
  public:
  Baz () : Foo(), Bar(this) {}
  virtual void parse_bar (Bar &) const { std::cout << "Hello World\n"; }
};

int main () {
  Baz baz;
  baz.parse_self();
}

これは、たまたま私のコンピューターで、コンパイラーを使用して動作します (いくつかのコンパイラーでテスト済み)。thisしかし、2003 年の標準のセクション 9.3.2 では、この方法を使用すると未定義の動作になるので、運が良かったのではないかと少し不安になります。厳密に言えば、イニシャライザ リストはコンストラクタの本体の外にあります。関連するテキストは次のとおりです。

9.3.2thisポインター非静的メンバー関数
本体では、キーワードthisは非左辺値式であり、その値は関数が呼び出されるオブジェクトのアドレスです。

私の使用法は合法で明確に定義されていますか、それとも未定義の動作ですか?

4

2 に答える 2

5

この場合、注意しなければならない点が2つあります。

まず、コンストラクタ初期化子リストで、thisポインタは構築されていない(または完全に構築されていない)オブジェクトを参照します。そのようなポインタにアクセスすることはOKですが、それが参照するオブジェクトは限られた方法でしか使用できません。言語仕様の12.7を参照してください。

次に、特定の例では、実際に行っているのは、アクセスを試みる前にthisポインターを型に変換することです。その瞬間までにサブオブジェクトが完全に構築Foo *されるため、これは完全に安全です。Foo(アクセスが続く場合は、完全に構築されたFooサブオブジェクトのみに制限されると思います)。

唯一の懸念は、この場合、タイプへの変換が合法であるthisFoo *どうか、つまり変換プロセス自体が成功するかどうかです。答えは次のとおりです。はい、通常の(非仮想)継承の場合、そのような変換は完全に合法で安全です(ここでも、12.7で明示的に許可されています)

于 2012-07-04T18:15:11.413 に答える
3

それは結構です。C ++標準ではthis、初期化子リストでのポインターの使用が実際に明確にされています。

12.6.2ベースとメンバーの初期化[class.base.init]

パラグラフ7:expression-list aの名前は、が指定されてmem-initializer いるコンストラクターのスコープで評価されmem-initializerます。[例:

    class X {
        int a;
        int b;
        int i;
        int j;
    public:
        const int& r;
        X(int i): r(a), b(i), i(i), j(this->i) {}
    };

X::rを参照するようX::aに初期化しX::b、コンストラクターパラメーターの値でi初期化X::iし、コンストラクターパラメーターの値でi初期X::jX::iし、 ;の値で初期化します。X これは、クラスのオブジェクトが作成されるたびに行われます。] [注:mem-initializerコンストラクターのスコープで評価されるため、thisポインターをaの式リストで使用して、mem-initializer初期化されるオブジェクトを参照できます。]

thisの初期化子のポインタの型Bazは、実際には型Bazです。もちろん、すべてのメンバーが初期化されているわけではないという事実に注意する必要があります。thisすべてではないにしても、多くのコンパイラが最高の警告レベルに設定されていると(とにかく実際に実行する必要があります) 、基本クラスへのポインタを渡すことについて警告します。

ただし、必要以上に複雑にしているようです。なぜ仮想関数parse_bar()Barクラスに入れて、クラスを忘れないのFooですか?

#include <iostream>

class Bar
{             
public:      
    Bar() {}      
    virtual ~Bar() {}      
    void parse_self () { parse_bar(); } 
private: // Template method pattern
    virtual void parse_bar() const = 0;         
};        

class Baz : public Bar
{      
public:      
    Baz () {}     
private: // Yes, this does override the private `parse_bar()` member!
    virtual void parse_bar() const { std::cout << "Hello World\n"; }      
};

int main ()
{ 
    Baz baz; 
    baz.parse_self(); 
} 

これは基本的に同じ機能を実行しますが、コードが少なくなります。

于 2012-07-04T18:13:45.537 に答える