C++学习:初始化列表、对象的构造顺序、对象的销毁

一、初始化列表

类中定义const成员

如果类中有const成员,那const成员必须先初始化,初始化类中的const成员,就需要初始化列表。

C++初始化列表对成员变量进行初始化的语法规则

//v1对m1进行初始化,v1,v2对m2进行初始化,v3对m3进行初始化
ClassName::ClassName():m1(v1), m2(v1,v2), m3(v3)
{
    //初始化操作
}

程序实例1:const成员变量初始化

#include 

class Test
{
private:
	const int ci; //const成员变量需要初始化

public:
	Test() : ci(10) //在构造函数中进行变量初始化,用10来初始化ci
	{

	}

	int getCI() { return ci; }
};

int main()
{
	Test t;

	printf("t.ci = %d\n", t.getCI());
	return 0;
}

输出结果:

t.ci = 10

类成员的初始化

成员的初始化顺序与成员的声明顺序相同;

成员的初始化顺序与初始化列表中的位置无关;

初始化列表先于构造函数的函数体执行;

程序实例2:初始化列表的使用

#include 

class Value
{
private:
	int mi;
public:
	Value(int i)
	{
		printf("i = %d\n",i);
		mi = i;
	}
	int getI()
	{
		return mi;
	}
};

class Test
{
private:
	Value m2;
	Value m3;
	Value m1;

public:
	Test() :m1(1),m2(2),m3(3) //初始化列表
	{
        printf("Test::Test()\n");
	}

};

int main()
{
	Test t;

	return 0;
}

输出结果:

i = 2
i = 3
i = 1
Test::Test()

结果分析,因为在Test类中声明Value对象的顺序是m2、m3、m1,即使初始化的顺序是m1、m2、m3也不行,而且初始化列表的执行顺序先于构造函数体本身。

二、对象的构造顺序

类可以定义多个对象,对象的构造顺序是怎样的

局部对象的构造顺序:当程序执行流到达对象的定义语句时进行构造

程序实例3:局部对象的构造顺序

#include 

class Test
{
private:
	int mi;
public:
	Test(int i)
	{
		mi = i;
		printf("Test(int i):%d\n", mi);
	}

	Test(const Test& obj)
	{
		mi = obj.mi;
		printf("Test(const Test& obj):%d\n", mi);
	}
};

int main()
{
	int i = 0;
	Test a1 = i; //1、首先构造,打印 Test(int i):0

	while (i < 3)
	{
		Test a2 = ++i; //2、反复构造3次
	}

	if (i < 4)
	{
		Test a = a1; //3、拷贝构造函数,打印Test(const Test& obj):0
	}
	else
	{
		Test a(100);
	}

	return 0;
}

堆对象的构造顺序

当程序执行流到达new语句时创建对象;

使用new创建对象将自动触发构造函数的调用;

程序实例4:堆对象的构造顺序

#include 

class Test
{
private:
	int mi;
public:
	Test(int i)
	{
		mi = i;
		printf("Test(int i):%d\n", mi);
	}

	Test(const Test& obj)
	{
		mi = obj.mi;
		printf("Test(const Test& obj):%d\n", mi);
	}
};

int main()
{
	int i = 0;
	Test* a1 = new Test(i);

	while (++i < 10)
	{
		if (i % 2)
		{
			new Test(i);
		}
	}

	if (i < 4)
	{
		new Test(*a1);
	}
	else
	{
		new Test(100);
	}

	return 0;
}

输出结果:

Test(int i):0
Test(int i):1
Test(int i):3
Test(int i):5
Test(int i):7
Test(int i):9
Test(int i):100

全局对象的构造顺序

全局对象的构造顺序是不确定的;

不同的编译器使用不同规则确定的构造顺序;

既然这样就没必要写代码了。

三、对象的销毁(析构函数)

对象被初始化之后才能使用,对象不需要了就销毁,销毁前需要做一些清理工作。C++中如何清理需要被销毁的对象。

解决方案:

为每个类都提供一个public的free函数;

对象不再需要时立即调用free函数进行清理;

但是这种方案存在明显的问题:free只是一个普通的函数,必须显式的调用,对象销毁前如果没有做清理,就可能造成资源泄露。

C++编译器能否自动调用某个特殊的函数进行对象的清理?答案是肯定的。

析构函数

C++中可以定义一个特殊的清理函数叫做析构函数析构函数的功能与构造函数相反。

析构函数定义:~ClassName();

析构函数没有参数,也没有返回值类型声明;

析构函数在对象被销毁时自动被调用。

程序实例5:析构函数的调用

#include 

class Test
{
public:
	Test()
	{
		printf("调用Test()\n");
	}

	~Test()
	{
		printf("调用~Test()\n");
	}
};


int main()
{
	Test t;

	return 0;
}

输出结果:

调用Test()
调用~Test()

结果分析

当定义了t时,构造函数就被调用,t只是个局部变量,用完之后是要被清理的,在return 之前就调用了析构函数。

程序实例6:堆变量的销毁

#include 

class Test
{
public:
	Test()
	{
		printf("调用Test()\n");
	}

	~Test()
	{
		printf("调用~Test()\n");
	}
};


int main()
{
	Test* pt = new Test();

	delete pt;

	return 0;
}

输出结果:

调用Test()
调用~Test()

你可能感兴趣的:(C++,c++,学习,开发语言)