一、统一初始化(Uniform Initialization)

(一)C++四种初始化方式

 1. 小括号:int x(0);     //C++98

 2. 等号:int x = 0;      //C++98

 3. 大括号:int x{0};     //C++98成功,C++11成功

 4. 等号和大括号:int x = {0}; //C++98失败,C++11成功

(二)统一初始化(也叫大括号初始化

 1聚合类型定义与大括号初始化

(1)聚合类型的定义

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

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

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

    B.无private或protected的非静态数据成员。

    C.不能有{}和=直接初始化的非静态数据成员“就地”初始化。(C++14开始己允许这种行为)。

(2)初始化方式:将{t1,t2…tn}内的元素逐一分解并赋值给被初始化的对象,相当于为该对象每个元素/字段分别赋值。(注意不会调用构造函数)

2非聚合类型的大括号初始化:调用相应的构造函数

3、注意事项

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

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

(三)大括号初始化的使用场景

   1、为类非静态成员指定默认值。//成员变量不支持使用小括号初始化

   2、为数组或容器赋值,如vector<int> vec = {1,2,3,4}; //不支持=()初始化。

   3、对不支持拷贝操作的对象赋值。如std::unique_ptr<int> p{};//不支持等号。

【编程实验】统一初始化聚合类型与非聚合类型的区别

#include <iostream>
#include <vector>
#include <map>
#include <atomic>

using namespace std;

//聚合类型(用{}初始化,相当于分别为各成员直接赋值,不会调用构造函数)
struct ST
{
    int x;
    double y = 0.0; //C++11失败,C++14通过
} st = { 1, 2 }; 

struct Foo
{
    int x;
    struct ST
    {
        int i;
        int j;
    } st;
public:
    int k = 0;
private:
    Foo() = default;    //C++14允许用default声明为默认构造函数,仍为POD类型。
    Foo(const Foo& foo) = default;
};

//非聚合类型(用{}初始化时,会调用相应的构造函数)
class Base{};
class Bar : Base  //1. 有基类,为非聚合类型
{
    double x;     //2. 有private普通成员,为非聚合类型
    static int k; //允许静态成员,但必须在类外定义用int Bar::k =0的方式初始化
public:
    int z;
    int y{ 0 }; //3. 通过=或{}来就地“就地”初始化,为非聚合类型
public:
    //4. 类中自定义构造函数,为非聚合类型
    Bar(double x, int y, int z): x(x), y(y), z(z)
    {
        cout << "Bar(double x, int y, int z)" << endl;
    }

    Bar(const Bar& bar) //自定义拷贝构造函数,为非聚合类型
    {
        x = bar.x;
        y = bar.y;
        z = bar.z;
    }
    virtual void func() {}; //5. 存在虚函数,为非聚合类型
};

int Bar::k = 0; 

//x,y究竟为0,0还是123,321?
//由于非聚合类型,是调用构造函数初始化的。即会将实参123、321传入Test(int,int)
//中,但该函数未使用这个实参,而是直接用0来初始化x和y。
struct Test 
{
    int x;
    int y;
    Test(int, int):x(0),y(0){}

} t = { 123, 321 };  //t.x = ?,  t.y=?

int main()
{
    //1.四种初始化方式对比
    int a = 0;      //等号=初始化 C++98
    int b(2 + 3);   //小括号直接初始化 C++98
    int c = { 0 };  //大括号等号初始化 C++98、C++11
    int d{ 0 };     //大括号直接初始化 C++11

    int i;   //未初始化
    int j{}; //j被初始化为0
    int* p;  //未初始化
    int* q{}; //j被初始化为nullptr

    int x[] = { 1, 3, 5 };    // C++98通过,C++11通过
    float y[4][3] = { {1, 3, 5},{2, 4, 6},{3, 5, 7},{4, 6, 8} };  // C++98通过,C++11通过
    int z[]{ 1, 3, 5 };       // C++98失败,C++11通过
    vector<int> v{ 1, 3, 5 }; // C++98失败,C++11通过
    map<int, double> m = { {1, 1.0f}, {2, 2.0f}, {3, 3.0f} };// C++98失败,C++11通过
    
    //2.聚合类型和非聚合类型的初始化的对比
    Foo foo = { 1,{2, 3} }; //POD类型,相当于从大括号中的值逐个赋值给foo对应的成员,不会调用构造函数。
    Foo foo2{ 4, 5, 6 };

    cout << "st.x = " << st.x << ", st.y = " << st.y << endl; //st.x=1, st.y=2;

    Bar bar = { 1, 2, 3 };  //非聚合类型,调用构造函数初始化: Bar(double,int,int)
    Bar bar2{ 1,2,3 };      //非聚合类型,调用Bar(double,int,int)构造函数
    
    //x,y究竟为0,0还是123,321?
    cout << "t.x = " << t.x << ", t.y = " << t.y << endl; //t.x = 0, t.y = 0,当统一初始化遇到构造函数时,优先调
                                                          //用构造函数初始化
    return 0;
}
/*输出结果
st.x = 1, st.y = 2
Bar(double x, int y, int z)
Bar(double x, int y, int z)
t.x = 0, t.y = 0
*/

二、初始化列表

(一)initializer_list的实现细节

template <class _Elem>
class initializer_list { // list of pointers to elements
public:
    using value_type      = _Elem;
    using reference       = const _Elem&;
    using const_reference = const _Elem&;
    using size_type       = size_t;

    using iterator       = const _Elem*;
    using const_iterator = const _Elem*;

    constexpr initializer_list() noexcept : _First(nullptr), _Last(nullptr) { // empty list
    }

    constexpr initializer_list(const _Elem* _First_arg, const _Elem* _Last_arg) noexcept
        : _First(_First_arg), _Last(_Last_arg) { // construct with pointers
    }

    _NODISCARD constexpr const _Elem* begin() const noexcept { // get beginning of list
        return _First;
    }

    _NODISCARD constexpr const _Elem* end() const noexcept { // get end of list
        return _Last;
    }

    _NODISCARD constexpr size_t size() const noexcept { // get length of list
        return static_cast<size_t>(_Last - _First);
    }

private:
    const _Elem* _First;
    const _Elem* _Last;
};
initializer_list源码(VC++2019)

相关文章:

  • 2022-02-13
  • 2021-08-28
  • 2021-06-07
  • 2021-09-05
  • 2022-02-20
  • 2021-10-13
  • 2021-11-17
猜你喜欢
  • 2021-11-06
  • 2022-12-23
  • 2022-12-23
  • 2022-01-13
  • 2021-10-27
  • 2022-03-02
  • 2022-12-23
相关资源
相似解决方案