在这种特定情况下,使用成员初始化列表和在构造函数中分配值之间是否存在差异?

时间:2011-01-03 23:06:10

标签: c++ initialization initialization-list

内部和生成的代码之间存在着真正的区别:

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}

MyClass::MyClass()
{
  _capacity=15;
  _data=NULL;
  _len=0
}

感谢...

12 个答案:

答案 0 :(得分:77)

您需要使用初始化列表来初始化常量成员,引用和基类

当您需要初始化常量成员,引用并将参数传递给基类构造函数时,如注释中所述,您需要使用初始化列表。

struct aa
{
    int i;
    const int ci;       // constant member

    aa() : i(0) {} // will fail, constant member not initialized
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0) { ci = 3;} // will fail, ci is constant
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0), ci(3) {} // works
};

示例(非详尽)类/结构包含引用:

struct bb {};

struct aa
{
    bb& rb;
    aa(bb& b ) : rb(b) {}
};

// usage:

bb b;
aa a(b);

初始化需要参数的基类(例如,没有默认构造函数)的示例:

struct bb {};

struct dd
{
    char c;
    dd(char x) : c(x) {}
};

struct aa : dd
{
    bb& rb;
    aa(bb& b ) : dd('a'), rb(b) {}
};

答案 1 :(得分:58)

假设这些值是原始类型,那么不,没有区别。只有在将对象作为成员时,初始化列表才会有所不同,因为初始化列表不是使用默认初始化,而是使用赋值,而是允许您将对象初始化为其最终值。这实际上可以明显加快。

答案 2 :(得分:18)

是。在第一种情况下,您可以将_capacity_data_len声明为常量:

class MyClass
{
private:
    const int _capacity;
    const void *_data;
    const int _len;
// ...
};

如果您想在运行时计算它们的值时确保const - 这些实例变量,这将非常重要,例如:

MyClass::MyClass() :
    _capacity(someMethod()),
    _data(someOtherMethod()),
    _len(yetAnotherMethod())
{
}

const个实例必须在初始化列表中初始化,基础类型必须提供公共无参数构造函数(原始类型可以)。

答案 3 :(得分:6)

我认为这个链接http://www.cplusplus.com/forum/articles/17820/给出了一个很好的解释 - 特别是那些刚接触C ++的人。

intialiser列表更有效的原因是在构造函数体内,只进行赋值,而不是初始化。因此,如果您正在处理非内置类型,则在输入构造函数的主体之前已经调用了该对象的默认构造函数。在构造函数体内,您要为该对象赋值。

实际上,这是对默认构造函数的调用,然后是对复制赋值运算符的调用。初始化列表允许您直接调用复制构造函数,这有时会明显更快(回想一下初始化列表位于构造函数体之前)

答案 4 :(得分:5)

我将补充一点,如果你有类类型的成员没有可用的默认构造函数,初始化是构建类的唯一方法。

答案 5 :(得分:3)

一个很大的区别是赋值可以初始化父类的成员;初始化程序仅适用于在当前类范围内声明的成员。

答案 6 :(得分:2)

取决于所涉及的类型。

之间的差异相似
std::string a;
a = "hai";

std::string a("hai");

其中第二种形式是初始化列表 - 也就是说,如果类型需要构造函数参数或者使用构造函数参数更有效,则会有所不同。

答案 7 :(得分:1)

真正的区别归结为gcc编译器如何生成机器代码并放下内存。解释:

  • (阶段1)在init主体(包括init列表)之前:编译器为类分配所需的内存。这堂课还活着!
  • (阶段2)在init主体中:由于分配了内存,现在每个赋值都指示对已经退出的''initialized'变量的操作。

当然还有其他方法可以处理const类型的成员。但是为了缓解他们的生活,gcc编译器编写者决定设置一些规则

  1. const类型成员必须在init主体之前初始化。
  2. 在阶段1之后,任何写入操作仅对非常量成员有效。

答案 8 :(得分:1)

只有一种方法可以初始化基类实例和非静态成员变量,并使用初始化列表。

如果未在构造函数的初始值设定项列表中指定基本或非静态成员变量,则该成员或基数将被默认初始化(如果成员/基数是非POD类类型或非数组) -POD类类型)或否则未初始化。

一旦输入构造函数体,所有碱基或成员都将被初始化或保持未初始化(即它们将具有不确定的值)。构造函数体中没有机会影响它们的初始化方式。

您可以将新值分配给构造函数体中的成员,但不能分配给const成员或类类型的成员,这些成员已被设置为不可分配,并且无法重新绑定引用。

对于内置类型和一些用户定义的类型,在构造函数体中分配可能与在初始化列表中使用相同值初始化具有完全相同的效果。

如果您未在初始化列表中命名成员或基础并且该实体是引用,则类类型没有可访问的用户声明的默认构造函数,是const合格且具有POD类型或是POD类型或包含const限定成员(直接或间接)的POD类类型数组,然后程序格式不正确。

答案 9 :(得分:0)

如果您编写初始化列表,则可以一步完成所有操作;如果你不写一个启动器列表,你将做两个步骤:一个用于声明,一个用于指定值。

答案 10 :(得分:0)

构造函数中的初始化列表和初始化语句之间存在差异。 让我们考虑下面的代码:

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>

class MyBase {
public:
    MyBase() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

class MyClass : public MyBase {
public:
    MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
        std::cout << __FUNCTION__ << std::endl;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

class MyClass2 : public MyBase {
public:
    MyClass2::MyClass2() {
        std::cout << __FUNCTION__ << std::endl;
        _capacity = 15;
        _data = NULL;
        _len = 0;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

int main() {
    MyClass c;
    MyClass2 d;

    return 0;
}

当使用MyClass时,所有成员将在执行构造函数中的第一个语句之前初始化。

但是,当使用MyClass2时,当构造函数中的第一个语句执行时,不会初始化所有成员。

在稍后的情况下,当某人在初始化某个成员之前在构造函数中添加了一些代码时,可能会出现回归问题。

答案 11 :(得分:0)

这是我看不到其他人提到的一点:

class temp{
public:
   temp(int var);
};

temp类没有默认的ctor。当我们在另一个类中使用它时,如下所示:

class mainClass{
public:
 mainClass(){}
private:
  int a;
  temp obj;
};

代码将无法编译,导致编译器不知道如何初始化obj,因为它只有一个显式的ctor,它接收到一个int值,因此我们必须按以下方式更改ctor:

mainClass(int sth):obj(sth){}

因此,它不仅涉及const和引用!