首页 > 其他分享 >列表初始化

列表初始化

时间:2023-12-06 14:02:58浏览次数:23  
标签:初始化 聚合体 int 列表 Test 构造函数

文章参考:

爱编程的大丙 (subingwen.cn)
C++中有多种数据类型,如:变量、数组、对象。这些数据类型各自的初始化方式,并没有统一的方法。为了简化初始化过程,C++11提出了一种统一的初始化方法,即列表初始化

1. 统一的初始化

1.1 C++98

在C++98中,有两种情况可以使用列表初始化:

  • 普通的数组。
  • 可以直接使用内存拷贝函数memcpy()的对象。

EG:

// 数组初始化
int arr1[] = {1, 2, 3, 4, 5};
double arr2[] = {1.1, 1.2, 1.3};

// 对象的初始化
struct Person{
    int id;
    double salary;
}zhangSan{1, 3333};

1.2 C++11

在C++11中,列表初始化的运用变得更加的灵活。

EG:

  • 类使用成员初始化列表进行初始化:

    #include <iostream>
    using namespace std;
    
    class Test{
    private: 
        int _num;
    public:
        Test(int num);
        Test(const Test& test);
    };
    // 一般构造函数
    Test::Test(int num): _num(num){
        cout << "common construction function" << endl;
    }
    // 拷贝构造函数
    Test::Test(const Test& test){
        _num = test._num;
        cout << "copy construction function" << endl;
    }
    
    int main(void){
        Test t1(1);         // 调用一般构造函数
        Test t2 = 2;        // 这是一个特殊情况,见下文:
        Test t3 = Test(2);	// 和上一句本质上是一样的,见下文
        Test t4(t1);        // 调用拷贝构造函数
        Test t5 = t2;       // 调用拷贝构造函数
        Test t6 = {3};      // 成员初始化列表,调用一般构造函数
        Test t7{4};         // 成员初始化列表,调用一般构造函数
        Test t8 = {t1};     // 成员初始化列表,调用拷贝构造函数
        Test t9{t2};        // 成员初始化列表,调用拷贝构造函数
        return 0;
    }
    
    • Test t2 = 2:该指令会被编译器改写为Test t2 = Test(2)。注意:之后并没有调用拷贝构造函数或移动构造函数(即通过匿名对象(右值)来给左值变量进行初始化),这时C++语法的一种特例,编译器直接令t2Test(2)等同,完全等价于Test t2(2),因此只会调用一次普通的构造函数。综上:Test t2 = 2等价于Test t2(2)
  • new对象时使用成员初始化列表进行初始化:

    int * p = new int{520};
    double b = double{52.134};
    int * array = new int[3]{1,2,3};
    
    • p:指向了一个new的内存,并使用成员初始化列表初始化为520。
    • b:对匿名对象使用列表初始化后,再进行拷贝初始化。
    • array:在堆内分配了一块内存,使用成员初始化列表将的堆多个元素进行初始化。
  • 作用于函数返回值:

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    class Person{
    private: 
        int _id;
        string _name;
    public:
        Person(){}
        Person(int id, string name): _id(id), _name(name){}
        void show_msg();
    };
    void Person::show_msg(){
        cout << "id: " << _id << "\t" << "name: " << _name << endl;
    }
    // 使用成员初始化列表,直接匿名返回对象
    Person get_case(){
        return {1, "zhangSan"};
    }
    
    int main(void){
        Person p = get_case();
        p.show_msg();
        return 0;
    }
    

2. 列表初始化细节

列表初始化有两种情况:

  • 聚合体:可以直接进行初始化。
  • 非聚合体:必须要有对应的构造函数。

2.1 聚合体

聚合体可以直接使用列表初始化,无需对应的构造函数。

要成为聚合体,需要同时满足以下五个条件,否则就是非聚合体:

  • 没有自定义的构造函数。

  • 没有私有/保护属性的非静态成员。如果有静态成员,那么该静态成员不能通过列表初始化进行初始化,因为静态成员的初始化是先于类的初始化,在编译阶段完成的,而列表初始化是在运行阶段进行的,因此要对静态成员进行独立的初始化。

    struct Test1{
        int a;
        int b;
    private:
        int c;
    }t1{1, 2, 3};		// error,因为存在私有/保护属性的非静态成员,无法使用初始化列表进行初始化。
    
    struct Test2{
        int a;
        int b;
    private:
        static int c;
    }t1{1, 2, 3};		// error,因为	静态成员无法使用初始化列表进行初始化
    
    struct Test2{
        int a;
        int b;
    private:
        static int c;
    }t1{1, 2};		
    int Test2::c = 3;		// 需要对静态成员进行单独初始化。
    
  • 没有基类。

  • 没有虚函数。

  • 类中不能有使用{}=直接初始化的非静态成员(注意:从C++14开始,即使有,也可以使用列表初始化。

2.2 非聚合体

非聚合体也可以使用列表初始化,但前提是要有对应的构造函数,列表初始化会自动调用对应的构造函数进行初始化。

Eg:

#include <iostream>
using namespace std;

struct T1{
private:
    int _a;
public:
    int _b;
    T1(int a, int b): _a(a), _b(b){}
};

int main(void){
    T1 t = {1, 2};		// 列表初始化调用了自定义的构造函数
    return 0;
}

注意:

即使当前类的某个成员是非聚合体,当前类也可以是聚合体。也就是说:聚合体/非聚合体不是嵌套的概念。

#include <iostream>
using namespace std;

struct T1{
private:
    int _a;
public:
    int _b;
};

struct T2{
public:
    T1 t;
    int _a;
    int _b;
};

int main(void){
    T2 t = {{}, 1, 2};
    return 0;
}

这里尽管T2的成员变量t是非聚合体,但T2依旧是聚合体,在使用列表初始化时,通过{}来初始化t变量,其实就是调用了默认的无参构造函数。

3. std::initializer_list

3.1 概述

引入:有时我们在传递参数时,需要传递多个相同类型、数量不定的参数,这时我们可以使用数组、vectorlist等。在C++11中,为我们提供了一个更加轻量级的模板类std::initializer_list来实现这一功能。

特点:

  • 内部定义了iterator等容器必须的概念,且遍历时得到的迭代器是只读的。
  • 可以接收长度任意的初始化列表,但元素类型必须一致。
  • 有三个成员接口:
    • begin()
    • end()
    • size()
  • 只能被整体初始化或赋值。

3.2 作为普通函数参数

#include <iostream>
using namespace std;

void traversal(initializer_list<int> var){
    auto it = var.begin();
    for (; it != var.end(); ++temp){
        cout << *it << " ";
    }
    cout << endl;
}

int main(void){
    traversal({1, 2, 3, 4, 5});
    return 0;
}

std::initializer_list在遍历时得到一个只读的迭代器,因此如果想要修改值,只能通过值覆盖的方式。

std::initializer_list的效率较高,因为它并不是保存了初始化列表中元素的拷贝,而是仅仅存储了初始化列表中元素的引用。

3.3 作为构造函数

#include <iostream>
#include <string>
#include <vector>
using namespace std;

class Test
{
private:
    vector<string> m_names;
public:
    Test(initializer_list<string> list)
    {
        for (auto it = list.begin(); it != list.end(); ++it)
        {
            cout << *it << " ";
            m_names.push_back(*it);
        }
        cout << endl;
    }
};

int main(void)
{
    Test t({ "jack", "lucy", "tom" });
    return 0;
}

标签:初始化,聚合体,int,列表,Test,构造函数
From: https://www.cnblogs.com/beasts777/p/17879350.html

相关文章

  • pgsql数据库安装和初始化
    !/bin/bash获取当前目录的绝对路径current_directory=$(readlink-f"$PWD")echo'-------------------------pgsql安装开始-----------------------'创建文件仓库配置sudosh-c'echo"debhttp://apt.postgresql.org/pub/repos/apt$(lsb_release-cs)-pgdgma......
  • 关于java:Windows:如何获取所有可见窗口的列表,并将指定窗口置顶
    importcom.sun.jna.Native;importcom.sun.jna.Structure;importcom.sun.jna.win32.StdCallLibrary;importorg.apache.commons.lang3.StringUtils;importjava.util.*;publicclassBringToForeground{publicstaticvoidmain(String[]args){Bri......
  • 格林威治时间时区列表
       西时区中时区东时区UTC-11Pacific/MidwayUTC-11Pacific/NiueUTC-11Pacific/Pago_PagoUTC-10America/AdakUTC-10Pacific/HonoluluUTC-10Pacific/RarotongaUTC-10Pacific/TahitiUTC-9Pacific/MarquesasUTC-9America/AnchorageUTC-9America/J......
  • 三元表达式、列表、字典生成式和匿名函数
    一、三元表达式1、引入比价两个数的大小,正常函数写法defmy_max(a,b):ifa>b:returnaelse:returnbmy_max(1,2)2、三元表达式写法defmy_max(a,b):returnaifa>belsebres=my_max(1,2)print(res)3、语法结构:条件成立......
  • ue5 C++生成Actor时初始化变量
    分三步1.UGameplayStatics::BeginDeferredActorSpawnFromClass预生成actor2.给actor变量赋值3.UGameplayStatics::FinishSpawningActor最终生成actorFStringstrBPFileName="/Game/UltraDynamicSky/SkyMode.SkyMode_C";UClass*pClass=LoadClass<AActor>(thi......
  • python中级之列表推导式
    Python列表推导式是什么列表推导式是Python语言特有的一种语法结构,也可以看成是Python中一种独特的数据处理方式,它在Python中用于转换和过滤数据。其语法格式如下所示,其中[if条件表达式]可省略。[表达式for迭代变量in可迭代对象[if条件表达式]]注意:学......
  • 【7.0】列表字典推导式
    【一】语法列表推导式可以利用列表,元组,字典,集合等数据类型,快速的生成一个特定需要的列表。语法格式如下[表达式for迭代变量in可迭代对象[if条件表达式]]【二】列表推导式【1】为什么要用列表推导式?列表推导式为我们提供了一种从序列创建列表的简单途径,它书写......
  • 3、利用初始化好的虚拟机当作模板,用于克隆
    摘自:https://blog.51cto.com/mfc001/6408226 利用初始化好的虚拟机当作模板,用于克隆第一步:先拷贝个虚拟机当作模板[root@ubuntimages]#virt-clone-orocky8-f/var/lib/libvirt/images/rocky8-template.qcow2-nrocky8-templateAllocating'rocky8-templat......
  • 4、虚拟机单机、集群的克隆、删除脚本(以初始化好的虚拟机为模板)
    摘自:https://blog.51cto.com/mfc001/6408229 虚拟机克隆、删除脚本[root@ubunt~]#catclone.sh#!/bin/bash##./etc/init.d/functions(如果是ubuntu,注释此行)Red="\e[1;31m"Purple="\e[1;35m"Green="\e[1;32m"Blue="\e[1;3......
  • 在WPF应用中使用GongSolutions.WPF.DragDrop实现列表集合控件的拖动处理
    WPF应用中,控件本身也可以通过实现事件代码实现拖动的处理,不过如果我们使用GongSolutions.WPF.DragDrop来处理,事情会变得更加简单轻松,它支持很多控件的拖动处理,如ListBox,ListView,TreeView,DataGrid等源自ItemsControl的控件,本篇随笔介绍在工作流模块中拖动TreeView和DataGrid......