【问题标题】:Use of this in initializer list在初始化列表中使用 this
【发布时间】:2012-07-04 18:02:04
【问题描述】:

假设我有一个类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是一个非左值表达式,其值为调用该函数的对象的地址。

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

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

    【讨论】:

    • @GManNickG 不要! cmets 可能对新手有用
    • @Evgeni:对不起。 :) 新手!关于类继承的问题? -> Book list.
    • 谢谢,AndreyT。哇哦。这将帮助我摆脱僵局。
    【解决方案2】:

    没关系。 C++ 标准实际上阐明了 this 指针在初始化列表中的使用:

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

    第 7 段:mem-initializerexpression-list 中的名称在指定了 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 指针可以用在 mem-initializer 的表达式列表以引用正在被调用的对象 初始化。 ]

    Baz 的初始化器中的this 指针的类型实际上是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(); 
    } 
    

    它的功能基本相同,但代码更少。

    【讨论】:

    • +1,很好的答案,在计算机上。很难选择选择哪个答案。关于我发布的代码和您建议的修复:我的代码旨在说明用法;一个 MWE。它看起来不像我必须处理的代码。
    猜你喜欢
    • 2013-03-15
    • 2011-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-15
    • 1970-01-01
    相关资源
    最近更新 更多