c++类的初始化原理

一、初始化时带括号和不带括号的区别

class Test4 {

public:

	Test3 t3;
	Test4(){
        t3 = Test3();
		cout << "Test4" << endl;
	}
};
int main()
{
	Test3 t3;
	cout << "忽略上面" << endl;
	Test4 t4;
//	Test4 t4();  // 不过是声明一个返回值为Test4的空参函数,区别大了
	system("pause");
	return 0;
}

千万不要搞混带括号和不带括号的情况。创建类和声明函数不一样。

二、类成员中包含其他类时的初始化

上面的例子中输出的结果是什么呢

c++类的初始化原理_第1张图片

Test3构造了两次,说明Test3在Test4声明时就已经调用了构造函数

Test4分配内存时,会给Test3也分配内存(因为不是new出来的),所以会直接调用Test3的构造函数来创建。

如果不想这样的话,可以使用指针解决这个问题。变成Test3 *t3,这样不过是声明了一个指针而且没有new,只有在new的时候才会创建而调用构造函数。

 

三、初始化列表和构造函数的区别

Test3做了改动,看下面的例子

class Test3 {

public:
	int a;
	Test3() {
		a = 0;
		puts("Test3 constructor");
	}

	Test3(Test3 &t3) {
		this->a = t3.a;
		puts("Test3 copy constructor");
	}
	Test3& operator=(Test3 &t) {
		puts("Test3 assign operator");
		this->a = t.a;
		return *this;
	}
	~Test3() { }
};

class Test4 {
public:

	Test3 t3;
	//Test4(){
	//	t3 = Test3();
	//	cout << "Test4" << endl;
	//}
	// 1. 创建临时变量,传参时隐藏的对临时变量(形参)进行初始化,调用拷贝构造
	// 2. 走进构造函数中,因为t3不是指针而且没有分配内存,所以此时调用t3的默认构造函数来创建。
	// 3. 用创建好的临时变量进行赋值,调用t3的赋值重载
	//Test4(Test3 t3) 
	//{
	//	cout << "进入Test4构造函数里面了" << endl;
	//	this->t3 = t3;
	//};

	// 1. 不创建临时变量
	// 2. 走进构造函数中,因为t3不是指针而且没有分配内存,所以此时要调用t3的默认构造函数来创建。
	// 3. 用创建好的临时变量进行赋值,调用t3的赋值重载
	//Test4(Test3& t3)
	//{
	//	this->t3 = t3;
	//};

	// 1.传参时隐藏的对临时变量进行初始化,调用拷贝构造
	// 2.在初始化列表中对成员变量进行初始化,调用拷贝构造
	// 3. 因为成员变量已经初始化了,所以不需要再创建一次。
	//Test4( Test3 t3) : t3(t3) 
	//{}

	// 1.在初始化列表中对成员变量进行初始化,调用拷贝构造
	//Test4( Test3 &t3) : t3(t3) 
	//{}

	Test4(Test3 t3) : t3(t3)
	{
		this->t3 = t3;
	};

};
int main()
{
	Test3 t3;
	cout << "忽略上面" << endl;
	Test4 t4(t3);
//	Test4 t4(); // 不过是声明一个返回值为Test4的空参函数,区别大了
	system("pause");
	return 0;
}

不同的构造函数依次的结果是:

c++类的初始化原理_第2张图片

c++类的初始化原理_第3张图片

c++类的初始化原理_第4张图片

最后一次的结果呢?

	Test4(Test3 t3) : t3(t3)
	{
		this->t3 = t3;
	};

答案是:

c++类的初始化原理_第5张图片

总结:

1. 函数参数在值传递时,其实是用的初始化的形式对临时变量进行赋值的,所以会调用拷贝构造函数。

2. 初始化和赋值的区别。

(1)对内置类型的成员(比如int,char等)没有什么的区别

(2)针对自定义的类。初始化调用的是拷贝构造函数,如果没有自己定义的话,是按照位来赋值的。所以成员变量中有指针可能会有问题,这种情况建议写自己的深拷贝构造函数。赋值时用=,如果未重载,走编译器自己默认的。

3. 初始化顺序是:

(1)先对形参(临时变量)赋值

(2)然后对初始化列表进行拷贝赋值(所以const和引用可以直接在这里赋值,下面会说)

(3)然后进入构造函数体中,查看所有的成员变量是否已经初始化,如果没有初始化的进行初始化,调用对应构造函数。

(4)之后就是显示的赋值等内容了,会调用重载的赋值运算符。

4. 初始化列表直接初始化,少了一次成员对象创建的过程,所以效率较高

5. 补充一点,成员是按照他们在类中出现的顺序进行初始化的,而不是按照他们在初始化列表出现的顺序初始化的

四、静态数据成员和const成员

详情参考:https://blog.csdn.net/theprinceofelf/article/details/20057359

 

你可能感兴趣的:(c++)