欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

构造和析构函数

程序员文章站 2022-06-01 13:03:02
...

1、前言

创建一个对象时常常需要做某些初始化工作,例如对数据成员赋初值,
注意:类的数据成员是不能在声明类的时候初始化的
为了解决这个问题,C++编译器提供了构造函数来处理对象的初始化,构造函数是一种特殊的成员函数,与其它成员函数不同,不需要用户来调用它,而是在建立对象是自动执行。

2、构造和析构函数

有关构造函数
1构造函数定义及调用
1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;
2)构造函数在定义时可以有参数;
3)没有任何返回类型的声明。
2构造函数的调用
自动调用:一般情况下C++编译器会自动调用构造函数
手动调用:在一些情况下则需要手工调用构造函数

有关析构函数
3)析构函数定义及调用
1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数
语法:~ClassName()
2)析构函数没有参数也没有任何返回类型的声明
3)析构函数在对象销毁时自动被调用
4)析构函数调用机制
C++编译器自动调用

#include <iostream>
using namespace std;

class Test {
public:
	Test()//无参数 无返回值
	{
		cout << "我是构造函数,被调用了" << endl;
	}
	~Test()
	{
		cout << "我是析构函数,被执行了" << endl;
	}
};
//给对象一个舞台,让对象被调用
void objplay()
{
	Test t1;
}
int main()
{
	
	objplay();

	system("pause");
	return 0;

}

构造和析构函数

3、C++编译器构造析构方案 PK 对象显示初始化方案

设计构造函数和析构函数的原因
面向对象的思想是从生活中来,手机、车出厂时,是一样的。
生活中存在的对象都是被初始化后才上市的;初始状态是对象普遍存在的一个状态的
普通方案:
为每个类都提供一个public的initialize函数;
对象创建后立即调用initialize函数进行初始化。
优缺点分析
1)initialize只是一个普通的函数,必须显示的调用
2)一旦由于失误的原因,对象没有初始化,那么结果将是不确定的
没有初始化的对象,其内部成员变量的值是不定的
3)不能完全解决问题

4、构造函数的分类及调用

/* 构造函数的分类和调用 */
class Test2 {
public:
	Test2()//无参数 无返回值
	{
		m_a = 0;
		m_b = 0;
		cout << "无参数构造函数" << endl;
	}
	Test2(int a)//无参数 无返回值
	{
		m_a = a;
		m_b = 0;
		cout << "一个参数的构造函数" << endl;
	}

	Test2(int a,int b)// 无返回值
	{
		m_a = a;
		m_b = b;
		cout << "两个参数的构造函数" << endl;
	}

	//赋值构造函数(拷贝构造函数)
	Test2(const Test2& obj)// 无返回值
	{

		cout << "我也是构造函数" << endl;
	}

	~Test2()
	{
		cout << "我是析构函数,被执行了" << endl;
	}

public:
	void printT()
	{
		cout << "普通成员函数 " << endl;
	}
private:
	int m_a;
	int m_b;

};
int main()
{
	
	//Test2 t1;	//默认调用无参数的构造函数

	/* 调用有参构造函数 有三种方法 */
	//方法1 括号法 必须加上参数		C++编译器自动调用
	//Test2 t1(1, 2);

	//方法2 
	//Test2 t1 = (3, 4);	//调用的是一个参数的构造函数 因为等号右边是一个等号表达式值是4  C++编译器自动调用
	//t1.printT();

	//方法3 直接手动调用构造函数
	Test2 t1 = Test2(3, 4);

	system("pause");
	return 0;

}

5、赋值构造函数(copy构造函数)的调用时机

第一二种调用时机

#include <iostream>
using namespace std;
class Test2 {
public:
	Test2()//无参数 无返回值
	{
		m_a = 0;
		m_b = 0;
		cout << "无参数构造函数" << endl;
	}
	Test2(int a)//无参数 无返回值
	{
		m_a = a;
		m_b = 0;
		cout << "一个参数的构造函数" << endl;
	}

	Test2(int a, int b)// 无返回值
	{
		m_a = a;
		m_b = b;
		cout << "两个参数的构造函数" << endl;
	}

	//赋值构造函数(拷贝构造函数)
	Test2(const Test2& obj)// 无返回值
	{

		cout << "我也是构造函数" << endl;
		m_b = obj.m_b + 100;
		m_a = obj.m_a + 100;
	}

	~Test2()
	{
		cout << "我是析构函数,被执行了" << endl;
	}

public:
	void printT()
	{
		cout << "普通成员函数 " << endl;
		cout << "m_a " << m_a << " m_b " << m_b << endl;
	}
private:
	int m_a;
	int m_b;

};

/* 赋值构造函数  用一个对象去初始化另一个对象 */
int main()
{
	Test2 t1(1, 2);
	Test2 t0(1, 2);

	/* 第一种调用时机 */
	//Test2 t2 = t1; //  =操作会调用t2的拷贝构造函数,用t1来初始化t2
	//t0 = t1;	//用t1给t0赋值  两个=的意义不同 赋值操作和初始化是两个不同的概念 赋值操作是不会调用拷贝构造函数的
	
	/* 第二种调用时机 */
	Test2 t2(t1);	//直接用t1初始化t2 这种方法是直接调用t2的拷贝构造函数 
	t2.printT();

	system("pause");
	return 0;
}

构造和析构函数

第三种调用时机

#include "iostream"
using namespace std;

class Location
{
public:
	Location(int xx = 0, int yy = 0)
	{
		X = xx;  Y = yy;  cout << "Constructor Object.\n";
	}
	//复制构造函数 完成对象的初始化
	Location(const Location & p) 	   
	{
		X = p.X;  Y = p.Y;   
		cout << "Copy_constructor called." << endl;
	}
	~Location()
	{
		cout << X << "," << Y << " Object destroyed." << endl;
	}
	int  GetX() { return X; }		
	int GetY() { return Y; }
private:   
	int  X, Y;
};

//业务函数 形参是一个元素
void f(Location  p)
{
	cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl;
}

void mainobjplay()
{
	Location a(1, 2);
	Location b = a;		//会调用b的拷贝构造函数 用a来初始化b

	cout << "b对象已经初始化完毕" << endl;

	f(b);//用实参b去初始化形参p 此时会调用拷贝构造函数  一共调用了3次构造函数,最后调用3次析构函数

}

void main()
{
	
	/* 第三种调用时机 */
	mainobjplay();
	system("pause");
}

构造和析构函数第四种调用时机

#include "iostream"
using namespace std;

class Location
{
public:
	Location(int xx = 0, int yy = 0)
	{
		X = xx;  Y = yy;  cout << "Constructor Object.\n";
	}
	//复制构造函数 完成对象的初始化
	Location(const Location & p) 	   
	{
		X = p.X;  Y = p.Y;   
		cout << "Copy_constructor called." << endl;
	}
	~Location()
	{
		cout << X << "," << Y << " Object destroyed." << endl;
	}
	int  GetX() { return X; }		
	int GetY() { return Y; }
private:   
	int  X, Y;
};

//业务函数 形参是一个元素
void f(Location  p)
{
	cout << "Funtion:" << p.GetX() << "," << p.GetY() << endl;
}

void mainobjplay()//第三种
{
	Location a(1, 2);
	Location b = a;		//会调用b的拷贝构造函数 用a来初始化b

	cout << "b对象已经初始化完毕" << endl;

	f(b);//用实参b去初始化形参p 此时会调用拷贝构造函数  一共调用了3次构造函数,最后调用3次析构函数

}

//业务函数 第四种调用时机用 返回一个元素
//结论1:函数的返回值是一个元素(复杂数据类型,返回的是一个新的匿名对象,所以会调用匿名对象类的拷贝构造函数
//结论2:有关匿名对象的去和留:关键看,返回时如何接
//如果用匿名对象初始化另一个同类型的对象,那么匿名对象直接转成这个有名对象
//如果用匿名对象赋值给另外一个同类型的对象,那么匿名对象会马上被析构
Location g()
{
	Location A(1, 3);
	//当返回一个类的对象时,会自动调用拷贝构造函数创建一个匿名对象
	return A;
}
//第四种调用时机的第一个场景
void objplay()
{
	g();
}
//第四种调用时机的第二个场景
void objplay2()
{
	//用匿名对象初始化m 此时C++编译器直接把这个匿名对象转成m (扶正 从匿名转有名)
	Location m = g();
	printf("匿名对象被扶正,不会被析构\n");
	cout << m.GetX() << endl;
}
void objplay3()
{
	//用匿名对象初始化m 此时C++编译器直接把这个匿名对象转成m (扶正 从匿名转有名)
	Location m2(1, 2);
	m2 = g();
	printf("此时因为用匿名对象=给m2,匿名对象将会被析构\n");
	cout << m2.GetX() << endl;
}
void main()
{
	//第四种调用时机
	//objplay2();
	objplay3();
	system("pause");
}

构造和析构函数

总结

构造和析构函数