浅墨浓香

想要天亮进城,就得天黑赶路。

导航

第6课 列表初始化(1)_统一初始化

Posted on 2017-10-04 21:34  浅墨浓香  阅读(1078)  评论(0编辑  收藏  举报

1. 统一初始化(Uniform Initialization)

(1)在C++11之前,很多程序员特别是初学者对如何初始化一个变量或对象的问题很容易出现困惑。因为可以用小括号、大括号或赋值操作符等多种方式进行初始化

(2)基于这个原因,C++11引入了“统一初始化”的概念。这意味着我们可以使用{}这种通用的语法在任何需要初始化的地方。

【实例分析】初始化列表

#include <iostream>
#include <vector>
#include <map>
#include <complex>
using namespace std;

//编译选项:g++ -std=c++11 test1.cpp -fno-elide-constructors

struct Test
{
    int x;
    int y;
} test  {123, 321}; //等价于test = {123,321}

int main()
{
    int i;   //未初始化
    int j{}; //j被初始化为0
    int* p;  //未初始化
    int* q{}; //j被初始化为nullptr
    
    int* a = new int{123}; //等价于int* x=new int(123);
    double b = double{12.12}; //等价于double(12.12)产生一个临时对象,再拷贝初始化
    int* arr = new int[3]{1, 2, 3}; //C++11中新增的初始化堆上数组的方式
    std::map<std::string, int> mm {{"2", 1},{"2", 2}, {"3", 3}}; //相当于map<string,int> mm = {...};
    
    int values[]{1, 2, 3}; //等价于int values[]={1, 2, 3};
    vector<int> v{2, 3, 5, 7, 11, 13, 17};

    complex<double> c{4.0, 3.0}; //等价于c(4.0, 3.0);
cout << test.x << endl; cout << test.y << endl; return 0; }

2. 列表初始化的使用细节

(1)引入初始化列表(initializer-list)出现的一些模糊概念

//x,y究竟为0,0还是123,321?
struct A
{
  int x;
  int y;

  A(int,int):x(0), y(0){} //非聚合类型,使用{}初始化时会调用相应的构造函数
} a = {123, 321};  //a.x=0, a.y=0

(2)聚合类型的定义

  ①类型是一个普通类型的数组(如int[10]、char[]、long[2][3])

  ②类型是一个类(class、struct或union),且:

    A.无基类、无虚函数以及无用户自定义的构造函数。

    B.无private或protected的普通数据成员(即非静态数据成员)。

    C.不能有{}和=直接初始化的非静态数据成员(“就地初始化”)

【实例分析】聚合类型与非聚合类型的初始化

#include <iostream>
using namespace std;

//x,y究竟为0,0还是123,321?
struct A
{
  int x;
  int y;

  A(int,int):x(0), y(0){} //非聚合类型,使用{}初始化时会调用相应的构造函数
} a = {123, 321};  //a.x=0, a.y=0

struct Base{};

//聚合类型的定义
struct Foo : public Base //不能有基类
{
private:
    double z;      //不能有private的普通成员
    static int k;  //ok,但必须在类外用int Foo::k = 0的方式初始化
public:
    Foo(int x, int y, double z):x(x),y(y),z(z) //不能有构造函数!
    {
        cout<< "Foo(int x, int y, double z)" << endl;
    } 
    
    Foo(const Foo& foo)  //不能有构造函数!
    {
        this->x = foo.x;
        this->y = foo.y;
        this->z = foo.z;
        
        cout<< "Foo(const Foo& foo)" << endl;
    }
    
    int x;
    int y; //不能通过int y=0;或int y{0}来"就地初始化"
    virtual void F(){}; //不能有虚函数!
        
};

int main()
{
    Foo f1(1, 2, 3.0);     //直接调用构造函数初始化
    Foo f2{4, 5, 6.0};     //由于Foo是个非聚合类型,使用{}时会调用相应的构造函数来初始化。
    Foo f3 = {7, 8, 9.0};  //非聚合类型会调用构造函数来初始化
    
    cout <<"a.x = " << a.x << ", a.y = " << a.y << endl;
    
    return 0;
}
/*输出结果
Foo(int x, int y, double z)
Foo(int x, int y, double z)
Foo(int x, int y, double z)
a.x = 0, a.y = 0
*/

(3)注意事项

  ①聚合类型的定义是非递归的。简单来说,当一个类的普通成员是非聚合类型时,这个类也有可能是聚合类型,也就是说可以直接用列表初始化。

  ②对于一个聚合类型可以直接使用{}进行初始化,这时相当于对其中每个元素分别赋值;而对于非聚合类型,则需要先自定义一个合适的构造函数才能使用{}进行初始化,此时使用初始化列表将调用它对应的构造函数。