在初始化列表中使用它

时间:2012-07-04 18:02:05

标签: c++ this language-lawyer initializer-list

假设我有一个类Baz,它按顺序继承自类FooBar。类Bar的构造函数接受指向Foo对象的指针。我想要做的是将this作为Foo对象传递给Bar构造函数:

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();
}

这恰好可以在我的计算机上运行,​​使用我的编译器(使用其中几个进行测试)。然而,2003标准的第9.3.2节让我有点不安,我可能会变得幸运,使用this这种方式是未定义的行为。严格地说,初始化列表在构造函数的主体之外。这是相关的文字,强调我的:

  

9.3.2 this指针
  在非静态成员函数的 body 中,关键字this是一个非左值表达式,其值是调用该函数的对象的地址。

我的使用是否合法且定义明确,还是未定义的行为?

2 个答案:

答案 0 :(得分:4)

在这种情况下,有两点需要注意。

首先,在构造函数初始化列表中,this指针指向一个非构造(或未完全构造)的对象。可以访问这样的指针,但它引用的对象只能以有限的方式使用。请参阅语言规范中的12.7。

其次,在您的具体示例中,您实际执行的操作是在尝试任何访问之前将this指针转换为Foo *类型。这是完全安全的,因为到那时Foo子对象已完全构建。 (我假设,无论访问将遵循什么,如果有的话,将仅限于完全构造的Foo子对象)。

唯一需要关注的是,将this转换为Foo *类型是否合法,即转换过程本身是否成功。答案是:是的,在普通(非虚拟)继承的情况下,这种转换是完全合法和安全的(再次,在12.7中明确允许)

答案 1 :(得分:3)

很好。 C ++标准实际上澄清了初始化列表中this指针的使用:

  

12.6.2初始化基础和成员[class.base.init]

     

第7段: expression-list mem-initializer中的名称在构造函数的范围内进行评估{{1} }已指定。 [实施例:

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::j 的值;这发生在每次类X::i的对象时   被建造。 ] [注意:因为评估了X   在构造函数的范围内,可以使用mem-initializer指针   表达式this的列表,用于引用对象   初始化。 ]

初始值设定项mem-initializer中的this指针的类型实际上是Baz类型。当然,您必须注意并非所有成员都已初始化。许多(如果不是全部)编译器设置在最高警告级别(你真的应该这样做)会警告你将Baz指针传递给基类。

然而,看起来你正在使它变得更加复杂。为什么不把虚拟函数this放在parse_bar()类中,忘掉Bar类?

Foo

这基本上具有相同的功能,但代码较少。