C++的各种初始化

本文详细介绍了C++中的初始化方法,包括默认初始化、值初始化、直接初始化与拷贝初始化、列表初始化以及构造函数中的成员初始化列表。强调了不同初始化方式在内置类型和类类型中的应用,特别是在处理const、引用、无默认构造函数的类成员以及继承关系时的规则和注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++的各种初始化

1.默认初始化

默认初始化是指定义变量时没有指定初值时进行的初始化操作。例如int a; Sales_data myData;等等。这些变量被定义了而不是仅仅被声明(因为没有extern关键字修饰),而且没有显式的赋予初值。特别的,如果采用动态分配内存的方式(即采用new关键字)创建的变量,不加括号时(如int *p=new int;)也是默认初始化,加了括号(如int *p=new int())为值初始化。变量的值与变量的类型与定义的位置有关系。

(1)对于内置类型变量(如intdoublebool等),如果定义在语句块外(即{}外),则变量被默认初始化为0;如果定义在语句块内(即{}内),变量将拥有未定义的值。

(2)对于类类型的变量(如string或其他自定义类型),不管定义于何处,都会执行默认构造函数。如果该类没有默认构造函数,则会引发错误。因此,建议为每个类都定义一个默认构造函数(=default)。

2.值初始化

值初始化是值使用了初始化器(即使用了圆括号或花括号)但却没有提供初始值的情况。例如,int *p=new int();vector vec(10);等等都是典型的值初始化方式。注意,当不采用动态分配内存的方式(即不采用new运算符)时,写成int a();是错误的值初始化方式,因为这种方式声明了一个函数而不是进行值初始化。如果一定要进行值初始化,必须结合拷贝初始化使用,即写成int a=int();值初始化和默认初始化一样,对于内置类型初始化为0,对于类类型则调用其默认构造函数,如果没有默认构造函数,则不能进行初始化。

3.直接初始化与拷贝初始化

直接初始化与拷贝初始化对应,其内部实现机理不同。直接初始化是指采用小括号的方式进行变量初始化(小括号里一定要有初始值,如果没提供初始值,那就是值初始化了!)。例如int a(12);Sales_data myData(para);vector ivec(ivec2);string s(“123456”);等等。拷贝初始化是指采用等号(=)进行初始化的方式。例如int a=12;string s=string(“123456”);等等。拷贝初始化看起来像是给变量赋值,实际上是执行了初始化操作,与先定义再赋值本质不同。

(1)对于内置类型变量(如intdoublebool等),直接初始化与拷贝初始化差别可以忽略不计。

(2)对于类类型的变量(如string或其他自定义类型),直接初始化调用类的构造函数(调用参数类型最佳匹配的那个),拷贝初始化调用类的拷贝构造函数。

特别的,当对类类型变量进行初始化时,如果类的构造函数采用了explicit修饰而且需要隐式类型转换时,则只能通过直接初始化而不能通过拷贝初始化进行操作。

4. 列表初始化(list initialization)/初始化器列表构造函数初始化(initializer-list constructor)

列表初始化(list initialization)是C++ 11 新引进的初始化方式,它采用一对花括号(即{})进行初始化操作。能用直接初始化和拷贝初始化的地方都能用列表初始化,而且列表初始化能对容器进行方便的初始化,因此在新的C++标准中,推荐使用列表初始化的方式进行初始化。列表初始化的应用场景有:int a{12};string s{"123"};vector vec{1,2,3};这里一定要注意,列表初始化使用的是花括号而不是圆括号!
列表初始化的一些规则:

  • 聚合类型可以进行直接列表初始化,对于一个聚合类型,使用列表初始化相当于对其中的每个元素分别赋值
    class tempC
    {
    public:
        int a;
        char b;
        double c;
    };
    tempC tempc{1, 'a', 3.21};
  • 对于非聚合类型,需要先自定义一个对应的构造函数,此时列表初始化将调用相应的构造函数
    std::initializer_list 是 C++11 引入的一种标准库类型,它允许你以列表的形式初始化对象,提供了一种简便的方式来使用大括号 {} 来统一初始化容器或其他类型等。std::initializer_list 主要用于支持统一初始化和类型安全的列表初始化。
    基本用法:
    std::initializer_list<T> 是一个模板类,其中 T 是元素的类型。它本质上封装了一个常量数组和一个指向该数组的指针。你可以通过传递一个初始化列表来创建 std::initializer_list 的对象。
    std::initializer_list 特别适合用于构造函数,它允许你通过花括号语法直接传递多个元素。例如:
#include <iostream>
#include <vector>

class MyClass {
public:
    MyClass(std::initializer_list<int> list) {
        for (auto value : list) {
            data.push_back(value);
        }
    }

    void print() const {
        for (auto value : data) {
            std::cout << value << " ";
        }
        std::cout << std::endl;
    }

private:
    std::vector<int> data;
};

int main() {
    MyClass obj = {1, 2, 3, 4, 5}; // 使用 initializer_list 初始化
    obj.print(); // 输出: 1 2 3 4 5
    return 0;
}

5. 构造函数使用成员初始器列表初始化
构造函数初始化列表/成员初始化列表(constructor initialize list/member initialization list)以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。

必须要使用构造函数初始化列表的几种情况

  • 类成员为const类型
  • 类成员为引用类型
#include <iostream>
class A
{
    public:
        A(int &v) : i(v), p(v), j(v) {}
        void print_val() { std::cout << "hello:" << i << "  " << j << std::endl;}
    private:
        const int i;
        int p;
        int &j;
};

int main(int argc ,char **argv)
{
    int pp = 55;
    A b(pp);
    b.print_val();
}

原因:

const对象或引用只能初始化但是不能赋值。构造函数的函数体内只能做赋值而不是初始化,因此初始化const对象或引用的唯一机会是构造函数函数体之前的初始化列表中。

从无到有叫初始化,初始化(调用拷贝构造函数)创建了新对象;赋值(调用赋值操作符)没有创建新对象,而是对已有的对象赋值。

  • 类成员为没有默认构造函数的类类型
#include <iostream>

class Base
{
    public:
        Base(int a) : val(a) {}
    private:
        int val;
};

class A
{
    public:
        A(int v) : p(v), b(v) {}
        void print_val() { std::cout << "hello:" << p << std::endl;}
    private:
        int p;
        Base b;
};

int main(int argc ,char **argv)
{
    int pp = 55;
    A b(pp);
    b.print_val();
}

原因同样是创建对象时,要初始类成员的每一个成员(如果没有在初始化列表里面,编译器会自动使用它的默认的构造函数进行初始化,但是它没有默认构造函数,所以会编译报错,所以没有默认构造函数的成员变量需要使用初始化列表进行初始化)

  • 如果类存在继承关系,派生类必须在其初始化列表中调用基类的构造函数
#include <iostream>

class Base
{
    public:
        Base(int a) : val(a) {}
    private:
        int val;
};

class A : public Base
{
    public:
        A(int v) : p(v), Base(v) {}
        void print_val() { std::cout << "hello:" << p << std::endl;}
    private:
        int p;
};

int main(int argc ,char **argv)
{
    int pp = 45;
    A b(pp);
    b.print_val();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值