4

假设我有一个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.2this指针
在非静态成员函数的主体this中,关键字是一个非左值表达式,其值是调用该函数的对象的地址。

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

4

2 回答 2

5

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

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

其次,在您的具体示例中,您实际做的是在尝试任何访问之前将this指针转换为类型。Foo *这是完全安全的,因为此时Foo子对象已完全构建。(我假设,如果有的话,任何访问都将仅限于完全构造的Foo子对象)。

this唯一关心的是这种情况下转换为类型是否合法Foo *,即转换过程本身是否应该成功。答案是:是的,在普通(非虚拟)继承的情况下,这种转换是完全合法和安全的(同样,在 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::aX::b用构造函数参数的值初始化,用构造函数参数的值初始化i用 的值初始化;每次 创建类对象时都会发生这种情况。] [注意:因为是在构造函数的范围内计算的,所以可以在 a 的表达式列表中使用指针来引用正在初始化的对象。]X::iiX::jX::iXmem-initializerthismem-initializer

this初始化程序中指针Baz的类型实际上是 type 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 回答