构造函数与析构函数基础,以及使用过程中的注意事项

构造函数与析构函数基础,以及使用过程中的注意事项_第1张图片

构造函数与析构函数的介绍

在C++中,对象创建后,需要进行初始化;在使用完对象后,也需要及时清理,否则会出现一些问题。(比如:对象在没有初始化时,其使用的后果是未知;而在使用完对象后,如果没有及时清理,会造成一定的安全问题。)

而C++利用了C++利用了构造函数和析构函数解决上述两个问题,这两个函数将会被编译器自动调用; 当然如果我们不提供构造和析构,编译器会提供编译器提供的构造函数与析构函数是空实现。

构造函数的意义:在创建对象时,为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用。
析构函数的意义:作用域对象小回迁系统自动调用,执行清理工作。

关于使用的语法,如下述代码段:


//构造函数语法: 类名(){}
/*
	构造函数没有返回值,也不写void
	函数名称与类名相同
	构造函数可以有参数,因此可以发生重载
	程序在调用对象时,会自动调用构造,无需手动调用,而且只会调用一次
*/

//析构函数的语法: ~类名(){}
/*
	析构函数没有返回值也不写void
	函数析构与类名相同,在名称前加上符号~
	析构函数不可以有参数!因此不能发生重载
	程序在对象销毁前会自动够细,无需手动调用,而且只会调用一次
*/

在学会其基础的语法后,就可以开始一些实例来进一步认识它们:

在public进行构造函数以及析构函数的书写;然后在函数中创建对象,运行函数后就可以输出构造函数与析构函数的输出值。

当然,正如上面所定义的,析构函数与构造函数会自动空运行(无论如何,它们肯定会在一个程序中运行的!);如果自己定义了它们,那么就会按照定义的那样来运行这两个函数。

class Person
{
	//构造函数   (无返回值,也不用写void),(函数名与类名相同!!), 构造函数可以有参数,因此可以发生重载,程序在调用对象时,会自动调用构造,无需手动调用,而且只会调用一次
public: //外界访问的前提
	Person()
	{
		cout << "调用构造函数" << endl;
	}

	//析构函数:
	/*
	析构函数没有返回值也不写void
	函数析构与类名相同,在名称前加上符号~
	析构函数不可以有参数!因此不能发生重载
	程序在对象销毁前会自动够细,无需手动调用,而且只会调用一次
	*/
	~Person()
	{
		cout << "调用析构函数" << endl;
	}



};

void test01()
{
	Person p;//这里测试可以发现,在test01中并没有调用构造函数Person,只是创建了一个对象;但是其自动调用了一次!
			 //析构函数也会自动调用一次,
			 //对象创建在栈区上,当数据执行完后,释放对象就会自动调用析构函数
}


int main() {

	test01();//这里测试可以发现,在test01中没有调用构造函数Person,但是其自动调用了一次!
	//如果把对象创建在main函数中,即Person,那么析构函就不会直接释放,因为要等整个main函数结束
	//所以在main函数中创建的对象,析构函数不会立即实现,程序会中断,所以在窗口显示不出来析构的调用

	cout << endl;
}

构造函数与析构函数都是存在的,且必定会调用,如果没有单独书写,那么编译器会使得其空实现
并且,在对象创建到main函数中时,析构函数不会在窗口发生
原因是:在main函数中,程序并没有执行结束,所以暂时只会运行构造函数,
然后在main函数结束,即窗口结束时,才会运行析构函数!此时窗口关闭,运行析构函数(因此看不到,不过在关闭的一瞬间,窗口依然是可以看得到析构函数的运行的!)
 

构造函数的分类与调用

分类如下:

//构造函数
//两种分类:
//1. 按参数分为: 有参构造和无参构造
//2. 按类型分为: 普通构造和拷贝构造
//
//三种调用方式:
//1. 括号法
//2. 显示法
//3. 隐式转换法

关于分类情况以及调用方式的介绍:

class Person
{	
public:
	//构造函数
	Person()//无参构造函数(默认构造函数)
	{
		cout << "Person的构造函数的调用:" << endl;
	}
	Person(int a)//有参构造函数()
	{
		age = 18;
		cout << "Person的有参构造函数的调用:" << endl;
	}

	//拷贝构造函数
	Person(const Person &p)	//把Person p这个对象来进行拷贝,将其属性全部拷贝到这个Person里
						    //但是注意,拷贝时,需要用const来修饰这个对象,来确保这个对象是不可修改的
							//同时,利用引用的方式去访问
	{
		//将传入的人的所有属性拷贝到当前的对象身上
		age = p.age;	//这里,一旦有参构造函数有一些初始化的值,那么拷贝构造时,也需要对成员进行初始化;
		cout << "拷贝构造函数的调用: " << endl;
	}


	~Person()
	{
		cout << "Person的析构函数的调用:" << endl;
	}

	int age;
};



//调用 构造函数:

void test01()
{
//1. 括号法————一般都是用括号法去调用构造函数,这样方便且更容易读
//1. 括号法————一般都是用括号法去调用构造函数,这样方便且更容易读

	Person p1;//默认构造函数的调用
	//那么怎么去调用构造函数的有参调用呢?

	Person p2(10);//有参构造函数的括号法调用

	//那怎么用括号法去调用拷贝构造函数呢?

	Person p3(p2);//直接根据其参数传递类型来确定调用的方法

	//注意事项:
	//注意事项:
	//调用默认构造函数的时候,不要加小括号!否则编译器会认为是一个函数的声明!

	cout << "p2的年龄:" << p2.age << endl;
	cout << "p3的年龄:" << p3.age << endl;//拷贝构造函数,将对象身上的所有属性进行拷贝

//2. 显示法

	Person p4;//默认构造函数的显示法调用

	Person p5 = Person(10);//有参构造函数的显示法调用

	//如何用显示法调用拷贝构造函数呢?
	Person p6 = Person(p2);//显示法调用拷贝构造函数

	//注意:Person(10);单独拿出来,被称作匿名对象(未命名);  特点:当前行结束后,系统会立即回收匿名对象(即释放空间)
	//注意:不要利用拷贝构造函数,初始化匿名对象
	//如Person(p3); 编译器会以为:Person p3,会认为这个过程是对象的声明!
	
//3. 隐式转换法

	Person p7 = 10;	//相当于写了: Person p4 = Person(10);//相当于有参构造的调用
	Person p8 = p4; //相当于写了: Person p8 = Person(p4);//相当于拷贝构造的调用


}

int main()
{

	test01();

	cout << endl;
	system("pause");
}

构造函数的调用时机

拷贝构造函数调用的时机(通常有3种情况)
1. 使用一个已经创建完毕的对象来初始化和一个新对象
2. 值传递的方式给函数参数传值
3. 以值方式返回局部对象

关于构造函数的调用时机(即何时使用它合适)



class Person {
public:
	Person()
	{
		cout << "Person的默认构造函数的调用:" << endl;
	}

	Person(int age)
	{
		cout << "有参构造函数的调用: " << endl;
		m_age = age;
	}

	Person(const Person& p)
	{
		cout << "拷贝构造函数的调用: " << endl;
		m_age = p.m_age;
	}

	~Person()
	{
		cout << "Person的析构函数的调用:" << endl;
	}

	int m_age;


};

//1. 使用一个已经创建完毕的对象来初始化和一个新对象

void test01()
{
	Person p1(20);
	Person p2(p1);//初始化一个新的对象
	cout << "p2的年龄为: " << p2.m_age << endl;
}

//2. 值传递的方式给函数参数传值

void dowork(Person p)//作为形参,来传递给函数;这里是直接进行调用拷贝构造函数
{
	
}


void test02()
{
	Person p;//默认构造函数的创建
	
	dowork(p);//这里传递的只是其中的值,如果在传递过后,即dowork函数里,值的大小发生变化;那么dowork的结果也不会变化

}



//3. 以值方式返回局部对象

Person dowork2()
{
	Person p1;
	return p1;//用值的方式返回的,因为是局部对象,所以创建后会释放空间(析构释放)
			  //因此返回的值只是拷贝出来的新的对象,然后返回(即调用拷贝构造函数)
}

void test03()
{
	Person p = dowork2();

}



int main()
{
	test01();

	test02();

	test03();

	system("pause");
}

上述代码段就是关于构造函数调用时机的实例。

如果尝试着去跑上述的代码段,可以发现其并没有输出什么实质性内容。

因为在运行某个对象时,必定会运行一次构造函数与析构函数,而在笔者书写的代码段里,构造函数与析构函数是自己定义的,所以会运行出结果(如果没有进行定义,那么就会空运行);如果自己不去定义它们,那么运行的结果就会是空运行(构造函数空运行定义,析构函数去清理空间)。

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