假设我有一个类Baz
,它按顺序继承自类Foo
和Bar
。类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
是一个非左值表达式,其值是调用该函数的对象的地址。
我的使用是否合法且定义明确,还是未定义的行为?
答案 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
这基本上具有相同的功能,但代码较少。