评估顺序

时间:2010-12-14 11:34:51

标签: c++ operator-precedence

我想知道这样的构造(初始化列表)是否有明确定义的EO(评估顺序):

struct MemoryManager
    {
        Pair* firstPair_;//<-beg
        Pair* currentPair_;
        Pair* lastPair_;//<-end

        MemoryManager():lastPair_(currentPair_ = firstPair_ = nullptr)
            {/*e.b.*/}
};

如果是的话,我个人更喜欢这种传统方式:

    MemoryManager():firstPair_(nullptr),
                    currentPair_(nullptr),
                    lastPair_(nullptr)
    {/*e.b*/}

5 个答案:

答案 0 :(得分:4)

正如约翰·迪布林所说,你的构造在技术上对于给定的具体例子是正确的,但是它很脆弱,并且对很多程序员来说很难理解。

脆性:

  • 如果声明顺序发生变化,可能会失败。

  • 如果更改了初始列表,则会失败。

要自己评估这样的结构,请记住这个想法:代码不是指示编译器进行出价,而是将意图传达给他人(也许是你以后的自己)。

因此,请尝试编写 clear 代码。

干杯&amp;第h。,

答案 1 :(得分:1)

即可。如您的代码所示,成员将按照它们在struct / class definition 中声明的相同顺序进行初始化(构造函数定义中初始化程序的顺序无关紧要,最多会出现警告告诉你他们的订单不正确)。

  

12.6.2§5:然后,非静态数据成员应按照在类定义中声明的顺序进行初始化(同样不管mem-initializers的顺序如何)。

请注意,这仅适用于属于同一访问说明符的变量,因此在public:说明符中找到的实例变量可以在private:中找到的变量之前或之后初始化}说明符(当然,struct计为public:说明符。)

编辑:以上段落不正确,我在考虑分配,而不是初始化:

  

9.2§12:声明没有插入访问说明符的(非联合)类的非静态数据成员,以便后面的成员在类对象中具有更高的地址。由access-specifier分隔的非静态数据成员的分配顺序未指定(class.access.spec)。

然而,更传统的方式有理由存在,即如果变量声明的顺序发生变化(例如,由于重构),代码将不会无声地破坏。人们不容易认为声明的顺序是相关的,除非警告另有说明。

答案 2 :(得分:1)

如果你想这样做,那就按照每个人立即理解的方式进行,而不必浏览标准:

MemoryManager()
  // no initialization here
{
  lastPair_ = currentPair_ = firstPair_ = nullptr;
}

但是,我真的没有看到这会给你买什么

MemoryManager()
  : lastPair_(), currentPair_(), firstPair_()
{}

只有大约六个字符完全相同。

答案 3 :(得分:0)

对于此特定示例,成员的初始化顺序无关紧要。以下构造函数与问题中的构造函数具有相同的行为:

MemoryManager():firstPair_(lastPair_ = currentPair_ = nullptr)
{/*e.b.*/}

这是因为成员是POD,因此根本没有构造函数默认初始化(12.6.2 / 4 C ++ '03):

  

如果给定的非静态数据成员或基类没有由mem-initializer-id命名(包括没有mem-initializer-list的情况,因为构造函数没有ctor-initializer),那么

     
      
  • 如果实体是(可能是cv限定的)类类型(或其数组)或基类的非静态数据成员,并且实体类是非POD类,则该实体是默认初始化的(8.5) 。如果实体是const限定类型的非静态数据成员,则实体类应具有用户声明的默认构造函数。
  •   
  • 否则,实体未初始化。如果实体是const限定类型或引用类型,或者是(可能是cv限定的)POD类类型(或其数组)包含(直接或间接)const限定类型的成员,则该程序是错误的形成。
  •   

对于上面的原始指针成员,“其他”子弹适用。

现在,即使成员确实有类型,请说:

class MbrType {
public:
  MbrType();
  MbrType(int *);
  MbrType(MbrType const &);
  MbrType & operator=(MbrType const &);
};

然后,编写它的构造函数将导致成员具有您期望的值,但是允许非优化编译器将构造函数实现为:

MemoryManager()
: firstPair_ ()  // implicit call to default constructor
, currentPair_ () // implicit call to default constructor
, lastPair_(currentPair_.operator=(firstPair_.operator=(MbrType (nullptr))))
{/*e.b.*/}

导致6次通话而不是3次。

答案 4 :(得分:-1)

,但没关系。您的代码不依赖于currentPair_firstPair_归零的顺序。