类与对象(中)

继上篇,今天带来类与对象(中)的文章

C++中,类里面会形成6个默认成员函数。当用户不去显式实现时,编译器会自动生成的成员函数,被称作默认成员函数。 接下来,一个个介绍。

在之前写线性表,栈实现时,我们都会用到Init和Destory的函数。C++中,出于简化和防止忘记,引出了构造函数和析构函数,对应实现Init和Destory的效果。

构造函数

构造函数的名字与类名相同,创建类的类型对象时由编译器自动调用。构造函数的任务是负责初始化对象,并不会去开空间创建对象。

接着聊聊构造函数的特性
先说基本的:①. 构造函数的函数名与类名相同 | ②.无返回值 | ③.对象实例化时,自动调用构造函数
④.构造函数也可以重载
⑤默认构造函数分为未被人为定义、编译器默认生成的构造函数和人为定义的 全缺省构造函数和无参构造函数。
⑥.C++把类型分为内置类型和自定义类型。int,char,double…就是内置类型,struct,union就是自定义类型。编译器生成的默认构造函数会对自定义类型调用其默认成员函数。
⑦.内置类型可以给初始值

class Time
{
public:
//无参构造函数
	Time()
	{

	}
	
//带参构造函数
	Time(const int hour,const int minute,const int second)
	{
		_hour = hour;
		_minute = minute;
		_second = second;
	}
	
//全缺省构造函数
	Time(const int hour = 1, const int minute = 1, const int second = 1)
	{
		_hour = hour;
		_minute = minute;
		_second = second;
	}

private:
	int _hour;
	int _minute;
	int _second;
//7.内置类型也可以在声明时给默认值
// private:
//	int _hour = 1;
//	int _minute = 1;
//	int _second = 1;
};
//4.这样我们就实现了3种不同的初始化方式,当然不写的话编译器也会自动去生成默认的构造函数
int main()
{
	Time t;
	return 0;
}

析构函数

构造函数是用来初始化的,析构函数并不是用来对对象本身进行销毁的。在对象被销毁时自动调用析构函数,来对对象中的资源进行清理工作,譬如动态开辟的内容进行销毁。

接下来,说说析构函数的特性
①构造的形式是在类名前加上~ | ②析构函数也没有返回值,与构造函数类似 | ③析构函数不能重载,与构造函数不同
④编译器生成的默认析构函数会对自定义类型调用其默认成员函数,这一点与构造函数类似
⑤对象生命周期结束时,系统会调用默认的析构函数
调用析构函数的顺序:局部对象(后定义的先析构,与栈类似)——> 局部的静态 ——> 全局对象(后定义的先析构)

在下面代码中,对象的销毁对象是怎样的呢?

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}

	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};

void func()
{
	static Stack s3(3);
	Stack s4(4);
}

Stack s6(6);
static Stack s7(7);

void TestStack()
{
	Stack s1(1);
	static Stack s2(2);
	func();
	Stack s5(5);
}

例如这个代码里头,(记住后定义的先析构),先被销毁的是func中的s4,再是s5,接着s1.到这就把局部内的对象给销毁了,再接着是局部的静态,那么先销毁s3,再销毁s2.到这就把局部静态的对象给销毁了。最后来销毁全局对象,那么还是后定义的先析构,所以先销毁s7,再将s6销毁。这就是整个过程整个顺序就是s4,s5,s1,s3,s2,s7,s6.

拷贝构造函数

特性:① 拷贝构造函数是构造函数的一个重载形式
②拷贝构造函数的参数只有一个且是类的类型对象的引用,通常用const修饰。并且这里不能传值,只能是引用,否则会引起无穷递归。

class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
   _year = year;
   _month = month;
   _day = day;
}
// Date(const Date& d)  // 正确写法
   Date(const Date d)  // 错误写法:编译报错,会引发无穷递归
{
   _year = d._year;
   _month = d._month;
   _day = d._day;
}
private:
   int _year;
   int _month;
   int _day;
};
int main()
{
   Date d1;
   Date d2(d1);
   return 0;
}

再给大家画图理解一下第二点。
首先,在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定
义类型是调用其拷贝构造函数完成拷贝的。

传值调用的话,这里的值是Date类型的,属于我们定义的类的类型,因此会继续去调用,造成无穷递归。
但,如果是引用调用的话,引用传递的是一个地址值,(与指针类似),是通过地址去找到值得,地址的话属于我们的指针类型,是一个简单类型,因此按简单类型的赋值拷贝,不会有拷贝构造函数的调用。
所以,我们定义拷贝构造函数时,必须以引用的参数来接收。
在这里插入图片描述
那么,我们知道,我们不显示声明拷贝构造函数时,系统会默认的去调用。大多数情况其实是可以的,但如果出现类中有指针数据成员时就有问题了。因为默认的拷贝构造函数,是将成员拷贝构造的,那么就会出现不同的两个指针指向同一块空间的情况。当一个实例销毁时,指针指向的那块空间就会被销毁。当销毁另一个实例,就会导致程序崩溃,因为此时的第二个指针已经无效了,那么再次去释放已经被释放的空间程序就会崩溃,相当于重复释放一块空间两次。 因此有些时候,拷贝构造函数还是得我们自己去声明定义。

赋值运算符重载

定义:C++为了增强代码的可读性引入了运算符重载。
形式为关键字operator后面接需要重载的运算符符号。
特性:①首先不能通过连接其他符号来创建新的操作符,必须是C和C++语法中存在的
②重载操作符必须有一个类类型参数
③.* \::\sizeof \?:\ . 注意以上5个运算符不能重载。 但是解引用*可以重载
作为类成员函数重载时,形参看起来比实际操作数少1,因为第一个参数是this指针,this指针是被隐藏的
赋值运算符只能重载成类的成员函数不能重载成全局函数
如果重载成全局函数的话,那类中的private类变量用不了,若直接放开成public也会出现不安全的情况。这里引入的办法一个是重载成类的成员函数,再一个是利用友元来解决,这个后续再提到,现在推荐的就是直接重载成类的成员函数。
⑥用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
这一点与拷贝构造函数类似,所以有些情况得我们自己去写赋值运算符重载函数。
否则呢,在赋值的时候,如果没有写赋值运算符重载,那直接将内容原封不动得拷贝过去,就会导致两个对象都指向同一块空间的情况不仅使得其中一个对象原本的空间丢失,在销毁时还会导致共同指向的空间被释放两次的情况。

⑦前置++和后置++

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	// 前置++
    //这里是前置++,通过引用返回来提高效率
	Date& operator++()
	{
		_day += 1;
		return *this;
	}
	// 后置++
	//这里就是形式上的不同。C++规定:后置++重载时多增加一个int类型的参数,但调用函数时
	//该参数不用传递,编译器自动传递
	Date operator++(int)
	{
		Date temp(*this);
		_day += 1;
		return temp;
	}
//注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份,
//而temp是临时对象,因此只能以值的方式返回,不能返回引用,与前置++不同!temp出这个作用域
//立马就要被销毁了,但前置++没有这个临时变量,是直接返回去的,就可以使用引用返回来提高效率。
private:
	int _year;
	int _month;
	int _day;
};
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值