在这种特定情况下,使用成员初始化器列表和在构造函数中分配值之间有区别吗?


90

在内部和生成的代码之间,真的有区别:

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

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

谢谢...

Answers:


60

假设这些值是原始类型,则没有差异。初始化列表仅在将对象作为成员时才有所不同,因为初始化列表使您可以将对象初始化为其最终值,而不是使用默认初始化后再进行赋值。实际上,这实际上可以更快。


17
就像Richard所说的,如果这些值是原始类型和const会有所不同,初始化列表是将值分配给const成员的唯一方法。
2011年

11
仅当变量不是引用或常量时,它才按所述方式工作,如果它们是常量或引用,则不使用初始化列表甚至无法编译。
stefanB 2011年

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) {}
};

4
如果_capacity_data_len有一流的类型,而一个可访问的默认构造?
CB Bailey

2
您可以调用可用的构造函数,如果需要设置更多的值,则可以从构造函数的主体中调用它们。区别在于您不能const在构造函数的主体中初始化成员,而必须使用初始化列表-非const成员可以在初始化列表或构造函数的主体中进行初始化。
stefanB 2011年

@stefan:您不能调用构造函数。像const成员一样,没有默认构造函数的类必须在初始化列表中初始化。
GManNickG'1

2
你还必须初始化初始化列表的引用
舍甫琴科Tylychko

2
@stefanB:如果我暗示您在实际操作时不明白这一点,我深表歉意。在您的回答中,您仅说明了何时必须在初始值设定项列表中命名基或成员,而没有解释在初始值设定项列表中进行初始化与在构造函数主体中进行赋值之间的概念差异实际上是我的误解可能来自。
CB Bailey

18

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

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

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

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

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


2
参考也一样。如果您的类具有引用成员,则必须在初始化列表中对其进行初始化。
Mark Ransom

7

我认为此链接http://www.cplusplus.com/forum/articles/17820/提供了很好的解释-特别是对于那些C ++新手。

初始化程序列表更有效的原因是,在构造函数主体内,仅发生分配,而不进行初始化。因此,如果要处理的是非内置类型,则在输入构造函数的主体之前,已调用该对象的默认构造函数。在构造函数主体内,您正在为该对象分配一个值。

实际上,这是对默认构造函数的调用,然后是对复制分配运算符的调用。初始化程序列表使您可以直接调用复制构造函数,这有时可能会明显更快(请注意,初始化程序列表位于构造函数的主体之前)



3

一个很大的区别是,该赋值可以初始化父类的成员。初始化程序仅适用于在当前类作用域声明的成员。


2

取决于所涉及的类型。两者之间的区别是相似的

std::string a;
a = "hai";

std::string a("hai");

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


1

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

  • (阶段1)在init主体(包括init列表)之前:编译器为该类分配所需的内存。全班都还活着!
  • (阶段2)在init主体中:由于已分配内存,因此每个分配现在指示对已经存在的“初始化”变量进行的操作。

当然,还有其他处理const类型成员的方法。但是为了减轻生活负担,gcc编译器作者决定设置一些规则

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

1

初始化基类实例和非静态成员变量的方法只有一种,那就是使用初始化程序列表。

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

输入构造函数主体后,所有基数或成员将已初始化或未初始化(即,它们将具有不确定的值)。构造函数主体中没有机会影响应如何初始化它们。

您可能能够向构造函数主体中的成员分配新值,但无法将其分配给不可分配的const成员或类类型的成员,并且无法重新绑定引用。

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

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


0

如果编写初始化程序列表,则一步一步完成;如果您没有编写初始化列表,则将执行2个步骤:一个用于声明,另一个用于赋值。


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时,执行构造函数中的第一条语句时不会初始化所有成员。

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


0

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

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

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

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

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

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

因此,它不仅仅是关于const和引用!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.