C++基础- 初始化列表和构造函数初始化区别

本文详细探讨了C++中初始化列表与构造函数初始化的区别,强调了初始化列表在处理const成员变量、引用类型及无默认构造函数的自定义类型成员变量时的必要性。初始化列表提供了更高的效率,并且初始化顺序遵循成员变量的定义顺序。使用初始化列表能够减少默认构造函数的调用,提高性能。
摘要由CSDN通过智能技术生成

本文详细讲解C++的初始化列表和构造函数初始化区别

目录

一、先看代码理解一下

二、再看定义

三、初始值列表的必要性

四、需要初始化列表实例

const的情况

引用类型的情况

没有默认构造的自定义类型成员变量

五、初始值列表的效率

六、始值列表初始化时顺序

七、使用初始化列表的好处


一、先看代码理解一下

//构造函数初始化
class MyClass
{
public:
	int m_a, m_b;
	MyClass(int a, int b) {
		m_a = a;
		m_b = b;
	}
};
//初始化列表
class MyClass2
{
public:
	int m_a, m_b;
	MyClass2(int a, int b) : m_a(a), m_b(b) {
	}
};

二、再看定义

初始化列表:与其他函数不同,构造函数除了有名字,参数列表和函数体之外,还可以有初始化列表,初始化列表以冒号开头,后跟一系列以逗号分隔的初始化字段。

关系:从定义中我们可以知道初始化列表也是配合构造函数使用的,也就是说,构造函数可以使用初始化列表,也可以不使用(简单理解为初始化列表是构造函数的配件,可以有,也可以没有,但是有些场合必须使用初始化列表,第三部分会说)。

区别:我们以前都认为构造函数是给对象初始化,其实严格来说构造函数体中的语句只能将其称为赋初值,而初始化列表才是真正的初始化。因为初始化是在一个变量(或对象)定义的时候赋予初始值才叫初始化。

三、初始值列表的必要性

因为类中有下面三种成员变量时,必须使用初始化列表来初始化

1、引用成员变量,因为引用必须在定义的时候初始化,而构造函数体中的语句是赋值,所以应该使用初始化列表初始化

2、const修饰的成员变量,因为const修饰的成员变量也必须在定义的时候初始化,所以必须使用初始化列表

3、没有默认构造的自定义类型成员变量,因为使用构造函数一定不可避免的会调用自定义类型成员变量的默认构造,然而使用初始化列表可以避免使用默认构造函数,直接调用该成员变量的拷贝构造

解析:对于第一个和第二个条件比较好理解,因为const本身在初始化后是无法再赋值的,所以必须使用初始化列表来对其初始化。

对于第二种情况,因为根据构造函数的执行顺序,在构造类B时必须先构造类A的构造函数,但类A并没有提供默认的构造函数,此时导致编译器找不到合适的构造函数,所以对象构造失败。因此这里必须在初始值列表中初始化。

四、需要初始化列表实例

const的情况

如果把MyClass中的变量int m_a, m_b改为const,会报错。

错误:

class MyClass
{
public:
	const int m_a, m_b;
	MyClass(int a, int b) {
		m_a = a;
		m_b = b;
	}
};

正确:

class MyClass
{
public:
	const int m_a, m_b;
	MyClass(int a, int b) : m_a(a), m_b(b) {
	}
};

引用类型的情况

错误

class MyClass3
{
public:
	int& m_a, m_b;
	MyClass3(int a, int b)
	{
		m_a = a;
		m_b = b;
	}
};

正确

class MyClass3
{
public:
	int& m_a, m_b;
	MyClass3(int a, int b) : m_a(a), m_b(b) {
	}
};

没有默认构造的自定义类型成员变量

错误

class MyClass4
{
public:

	MyClass4(MyClass& c)
	{
		cls = c;
	}
private:
	MyClass cls;没有默认构造的自定义类型
};

正确

class MyClass4
{
public:

	MyClass4(MyClass& c):cls(c)
	{
		
	}
private:
	MyClass cls;
};

五、初始值列表的效率

使用普通方式初始化时编译器会先执行一次对象的默认构造函数,然后才会对其赋值,初始化时执行了两个步骤。

#include <iostream>
class CAnimal
{
public:
    CAnimal() { std::cout << "default" << std::endl; }  // 添加默认初始化函数
    CAnimal(int weight) :m_weight(weight) {
    }
    int m_weight;
};
 
class CDog : public CAnimal {
public:
   //采用普通的会执行CAnimal默认的构造函数
	CDog(int weight) {
		m_weight = weight;
	}
	// 初始值列表,不会执行CAnimal默认的构造函数
//(和上面的构造函数不可共存)
	/*CDog(int weight) : CAnimal(weight) {
	}*/
};

执行CDog构造函数时会先执行CAnimal的默认构造函数,输出defualt 然后后才会执行m_weight = weight。但是对于初始值列表方式来说并不会执行两步,直接通过相应的构造函数初始化就完成了,相对来说简化了一个过程,效率肯定也会高一些。

六、始值列表初始化时顺序

使用初始值列表初始化时,初始化的顺序是根据成员变量定义的顺序来的,并不是初始值列表的顺序。

class CAnimal
{
public:
    CAnimal(int weight) :m_weight(weight) {
        std::cout << weight << std::endl;
    }
    int m_weight;
};
 
class CDog {
public:
    CAnimal m_a, m_b;
    CDog(int a, int b) : m_b(b), m_a(a){
    }
};

七、使用初始化列表的好处

当你了解了没有默认构造的自定义类型成员变量必须使用初始化列表的时候,初始化列表的好处也就出来了:可以减少一次默认构造的调用,当有大量对象的时候,使用初始化列表的好处还是很明显的,可以大大降低函数压栈带来的开销。所以我们能使用初始化列表尽量使用初始化列表。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

未来无限

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值