C++面向对象学习之路(二)构造函数与析构函数

如果变量在使用之前没有正确初始化或清除,将导致程序出错。(自我检讨一下,曾经因为没有对一个变量进行初始化就进行了使用,查了一晚上也没查出问题出在哪,后来发现后只想说,我是不是傻o(╥﹏╥)o )所以各位一定要切记对对象进行正确的初始化。
对对象进行初始化的一种方法是编写初始化函数,然而很多用户在解决问题时,常常忽视这些函数,以至于给程序带来了隐患。为了方便对象的初始化和清理工作,C++提供了两个特殊的成员函数:构造函数和析构函数。
构造函数的功能是在创建对象时,给数据成员赋初值,即对象的初始化。析构函数的功能是释放一个对象,在对象删除之前,用它来做一些内存释放等清理工作,它的功能与构造函数的功能正好相反。

2.1 构造函数

构造函数是一种特殊的成员函数。它的特点为对象分配空间,进行初始化,并且在对象创建时会被系统自动执行
定义构造函数原型的格式为:

类名(形参列表);

在类外定义构造函数的格式为:

类名::类名(形参列表)
{
//函数语句;
}

说明:

  • 构造函数名字必须与类名相同,否则系统会将它当成一般成员函数处理。
  • 构造函数可以有任意类型的参数,但是没有返回值,也不能指定为void类型。
  • 定义对象时,系统会自动的调用构造函数。
  • 通常构造函数被定义在公有部分。
  • 在实际应用中,通常要给每个类定义构造函数,如果没有定义构造函数,系统会自动的生成一个默认的构造函数,这个默认函数不带任何参数,只负责对象的创建,为对象开辟一个存储空间,而不做任何初始化工作,此时成员函数的值是随机的。
  • 构造函数可以重载。(重载这一概念会在后续章节介绍到)

【例】构造函数应用举例:输出日期

#include 
using namespace std;
class Data {
private:
 int year, month, day;
public:
 Data(int y, int m, int d);
 void Print();
};

int main()
{
 Data today(2019, 9, 10);
 count << "today is ";
 today.Print();
 return 0;
}

Data::Data(int y, int m, int d)
{
 year = y;
 month = m;
 day = d;
}

void Data::Print()
{
 cout << year << "-" << month << "-" << day << endl;
}

说明:主函数main没有显示调用构造函数Data,构造函数是在创建对象today时,系统自动调用的。即在创建对象today时,系统自动调用构造函数today.Data,并将数据成语year、month、day初始化为2019、9和10。

  • 构造函数可以不带参数,例如:
class Fruit{
private:
	int x,y;
public:
	Fruit();
	//......
};
Fruit::Fruit()
{
x=0;
y=0;
}
  • 构造函数也可以采用构造初始化表对数据成员进行初始化,例如:
class Data{
private:
	int year,month,day;
public:
	Data(int y,int m,int d):year(y),month(m),day(d);
	//构造函数初始化表对数据成员进行初始化
};
  • 如果数据成员是数组,则应该在构造函数中使用相应的语句进行初始化,例如:
class Fruit{
private:
	char name[20];
	int num;
public:
	Fruit(char name[ ],int n);
};
Fruit::Fruit(char na[ ],int n):num(n)
{
	strcpy(name,na);	//name是字符数组,所以用strcpy函数进行初始化
}

2.2 析构函数

在对象生存期结束前,通常需要进行必要的清理工作。这些相关的清理工作由析构函数完成。析构函数也是一种特殊的成员函数,当删除对象时就会调用析构函数,也就是在对象的生存期即将结束时,由系统自动调用,随后这个对象也就消失了。注意,析构函数的目的是在系统回收对象内存前执行清理工作,以便内存可被重新用于保存新对象。

定义析构函数的一般格式:

~类名();

例如:

class Fruit{
public:
~Data();
};

特点:

  • 析构函数名是由“~”和类名组成的。
  • 析构函数没有参数,也没有返回值,而且也不能重载。
  • 通常析构函数被定义在公有部分,并由系统自动调用。
  • 一个类中有且仅有一个析构函数。

说明:

  • 与类的其他函数成员一样,析构函数可以在类内定义,也可以在类外定义。如果不定义,系统会自动生成一个默认的析构函数:类名::~类名(){}。
  • 析构函数的功能是释放对象所占用的内存空间,析构函数在对象生存期结束前由系统自动调用。
  • 析构函数与构造函数两者的调用次序相反,即最先构造的对象最后被析构,最后构造的对象最先被析构。

【例】构造函数与析构函数的执行顺序 — Point类的多个对象的创建与释放。

#include 
using namespace std;
class Point
{
public:
 	Point(int a,int b);
 	~Point();
private:
 	int x, y;
};
int main()
{
	 Point p1(1,2), p2(3,4);
	 return 0;
}
Point::Point(int a,int b)	//定义构造函数
{
	 x = a;
	 y = b;
		 cout << "constructor......" << endl;
	 cout << "(" << x << "," << y << ")" << endl;
}
Point::~Point()	//定义析构函数
{
	 cout << "destructor......" << endl;
	 cout << "(" << x << "," << y << ")" << endl;
}

运行结果
C++面向对象学习之路(二)构造函数与析构函数_第1张图片
说明:调用构造函数的顺序与主函数main中创建对象的顺序一致,先创建对象p1,然后再创建对象p2;调用析构函数的顺序与创建对象的顺序相反,先析构对象p2,然后再析构对象p1。

  • 除了显式撤销对象时,系统会自动调用析构函数,在下列情况下,析构函数也会被调用:

①如果一个对象被定义在一个函数体内,则当这个函数结束时,该对象的析构函数会被自动调用。

#include 
using namespace std;
class Point
{
public:
	 Point(int a,int b);
 	~Point();
private:
 	int x, y;
};
void fun(Point p);
int main()
{
	cout << "inside main" << endl;
 	Point p1(1, 2);
 	fun(p1);
 	cout << "outside main" << endl;
 return 0;
}
Point::Point(int a,int b)
{
 	x = a;
 	y = b;
 	cout << "constructor......" << endl;
}
Point::~Point()
{
 	cout << "destructor......" << endl;
}
void fun(Point p)
{
	 cout << "inside fun" << endl;
}

运行结果
C++面向对象学习之路(二)构造函数与析构函数_第2张图片
说明:在主函数main中定义对象p1时,系统自动的调用p1的构造函数;当调用函数fun时,实参p1将值对应的赋给了形参p;当函数fun执行完,系统自动调用对象p的析构函数;当主函数main结束时,系统自动调用对象p1的析构函数。由此可见,只要对象超出它的作用域,系统就自动调用析构函数。

如果一个对象使用new运算符动态创建,在使用delet运算符释放它时,delet会自动调用析构函数(在程序中如果不显式撤销该对象,系统不会自动调用析构函数。也就是说new运算符动态创建的对象,如果不用delet运算符释放它,系统不会自动调用析构函数)详细用法将会在下节介绍到。

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