c++类初始化列表初探

时间:2020-04-22
本文章向大家介绍c++类初始化列表初探,主要包括c++类初始化列表初探使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

1 初始化和赋值

  • 初始化:创建一个对象并赋予一个初值;
  • 赋值:通过赋值运算符(=),将“=”右侧值赋给“=”左侧对象;
int a=5;   //创建一个int对象a,初始值为5
a=12;      //赋值

对于内置数据类型,初始化和赋值的区别不大;
对于自定义类型,初始化操作调用拷贝构造函数,赋值操作调用拷贝赋值运算符,下面以Person类为例;

//Person类
class Person {
public:
	Person()
	{
		cout << "Person 默认构造调用" << endl;
	}
	Person(const char *name,int age)
	{
		cout << "Person有参构造调用" << endl;
		this->m_Name = new char[strlen(name) + 1];
		strcpy_s(this->m_Name, strlen(name) + 1, name);
		this->m_Age = age;
	}
	Person(const Person&p)
	{
		cout << "Person 拷贝构造调用" << endl;
		if (this->m_Name != NULL)
		{
			delete[]this->m_Name;
			this->m_Name = NULL;
		}
		this->m_Name = new char[strlen(p.m_Name) + 1];
		strcpy_s(this->m_Name,strlen(p.m_Name)+1, p.m_Name);
		this->m_Age = p.m_Age;
	}
	Person&operator=(const Person &p)
	{
		cout << "Person拷贝赋值" << endl;
		if (this->m_Name != NULL)
		{
			delete[]this->m_Name;
			this->m_Name = NULL;
		}
		this->m_Name = new char[strlen(p.m_Name) + 1];
		strcpy_s(this->m_Name, strlen(p.m_Name) + 1, p.m_Name);
		this->m_Age = p.m_Age;
		return *this;
	}
	void showInfo()
	{
		cout << this->m_Name << "今年" << this->m_Age << "岁!" << endl;
	}
	~Person()
	{
		cout << "Person析构调用" << endl;
		if (m_Name != NULL)
		{
			delete[]m_Name;
			m_Name = NULL;
		}
	}
	char* m_Name;
	int m_Age;
};
//测试函数
void test()
{
	Person p("小明", 23);
	p.showInfo();
        cout<<endl;
	Person p2(p);//也可写为Person p2=p,初始化
	p2.showInfo();
        cout<<endl;
	Person p3;
	p3 = p;//赋值
	p3.showInfo();
        cout<<endl;
}

运行结果

1.1 结论

通过程序运行结果可以知道,Person p2(p)语句调用类的拷贝构造函数,由于Person类中的m_Name是指向堆区空间的数据,所以我们必须提供自定义的拷贝构造函数,因为默认的拷贝构造函数只是简单的值拷贝;p3 = p语句调用类的拷贝赋值,同理编译器会提供一个默认的拷贝赋值运算符,也是简单的值拷贝,所以我们必须提供自定义的拷贝赋值运算符;

2 构造函数初始化列表

我们知道,构造函数是用来初始化类对象的数据成员,编写构造函数一般有两种方式,一种是通过在函数体里赋值实现,另一种是通过初始值列表,对于内置数据类型,这两种方式的相差无几,但是如果该类的数据成员有自定义的类型,那么就效率上讲,两者有些差别;

class MyClass1 {
public:
	MyClass1()
	{
		cout << "myclass1 默认构造调用" << endl;
	}
	MyClass1(const MyClass1 &)
	{
		cout << "myclass1 默认拷贝构造调用" << endl;
	}
	MyClass1&operator=(const MyClass1 &)
	{
		cout << "myclass1 默认拷贝赋值调用" << endl;
		return *this;
	}
};
class MyClass2 {
public:
	MyClass2(int a,MyClass1 &c)
	{
		this->m_A = a;
		this->m_class = c;
	}
	//MyClass2(int a,MyClass1&c):m_A(a),m_class(c){}
	int m_A;
	MyClass1 m_class;
};
//测试函数
void test()
{
	MyClass1 my1;
	MyClass2 my2(3, my1);
}

运行结果

  1. 使用函数体内赋值
  1. 使用初始化列表

2.1 结论

通过运行结果可以知道对于自定义类型,通过初始化列表进行初始化,只需调用一次拷贝构造函数,而在构造函数中初始化,需要调用一次默认构造函数和一次赋值操作。

3 必须使用初始化列表的情况

如果类的数据成员是const,引用或者某种未提供默认构造函数的类类型,那么我们必须通过构造函数初始值列表为这些成员提供初值(c++prime第五版)

3.1 结论

建议养成使用构造函数初始值的习惯,这样能避免某些意想不到的编译错误

4 成员初始化顺序

构造函数初始值列表只说明用于初始化成员的值,并不限定初始化的具体顺序,成员的初始化顺序与它们在类定义中出现的顺序一致

class MyClass {
public:
	MyClass(MyClass2 m2, MyClass3 m3, MyClass4 m4) :mc3(m3), mc4(m4), mc2(m2) {}//这里的顺序并不影响mc2,mc3,mc4的初始化顺序
	//由下面出现的顺序决定,所以成员初始化顺序为:mc2->mc3->mc4
	MyClass2 mc2;
	MyClass3 mc3;
	MyClass4 mc4;
};

5 参考资料

1.《c++prime 第五版》
2. https://blog.csdn.net/gxnu/article/details/1832462?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1

原文地址:https://www.cnblogs.com/woshidaan-caw/p/12751774.html