类和对象中篇(通俗易懂)


前言

一个空类真的什么都没有吗?答案是否,它内部会自动生成6个默认成员函数(用户没有显示实现,编译器默认生成)。
在这里插入图片描述


一、构造函数:

1.概念:

我们先看一段代码:

class Date
{
    
    
public:
	void Init(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
    
    
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1;
	d1.Init(2022, 8, 1);//初始化
	d1.Print();
	return 0;
}

我们可以看到每次创建对象都需要调用Init初始化,并且日常使用很可能忘记初始化,那能否在创建对象时自动初始化呢?答案是可以,构造函数会在对象创建时自动调用初始化对象。

2.特点:

构造函数主要任务是初始化对象,在对象创建时由编译器自动调用。
特点:

  1. 函数名与类名相同。
  2. 对象实例化时编译器自动调用对应构造函数。
  3. 无返回值。
  4. 可以重载。
class Date
{
    
    
public:
	// 1.无参构造函数
	Date()
	{
    
    }
	// 2.带参构造函数
	Date(int year, int month, int day)
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1;//调用无参构造
	Date d2(2022, 8, 1);//调用有参构造
	return 0;
}
  1. 如果类里没有显示定义构造函数,编译器会自动生成。
    注意:编译器自动生成的默认构造函数对内置类型(int,char…)不处理,自定义类型会自动调用它的构造函数。
    如下图所示:B类实例化对象b1内部_n仍然为随机值,而_a为自定义类型已经初始化。

在这里插入图片描述
那如何让默认生成构造函数对内置类型同样初始化呢?
C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值。
使用方法如下:
在这里插入图片描述

二、析构函数:

析构函数与构造函数相反,它是完成对象的清理工作。
注意:析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作

特点:

  1. 析构函数名为类名前面带上~。
  2. 对象生命周期结束时自动调用。
typedef int DataType;
class Stack
{
    
    
	Stack(int capacity = 3)//构造函数
	{
    
    
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (nullptr == _array)
		{
    
    
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_top = 0;
	}
	~Stack()//析构函数
	{
    
    
		if (_array)
		{
    
    
			free(_array);
			_array = nullptr;
			_capacity = 0;
			_top = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _top;
};
  1. 无参数和返回值。
  2. 没有显示实现编译器也会自动生成。

和构造函数类似,析构函数对于自定义类型会自动调用它的析构函数,对于内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可。

注意:如果类中没有申请资源时,析构函数可以不写,比如Date类可以直接使用编译器生成的默认析构函数,有资源申请时,比如Stack类一定要写,否则会造成资源泄漏。

三、拷贝构造:

拷贝构造函数:只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

特点:

  1. 拷贝构造是构造函数的一种重载形式。故函数名也为类名。
  2. 拷贝构造形参为传引用调用,不能传值调用会引发无穷递归。
    在这里插入图片描述
class Date
{
    
    
public:
	Date(int year = 2022, int month = 8, int day = 17)//构造函数
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	//Date(const Date date)//错误写法
	Date(const Date& date)//拷贝构造
	{
    
    
		_year = date._year;
		_month = date._month;
		_day = date._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1(2022, 8, 1);//调用无参构造
	Date d2(d1);//拷贝构造1
	Date d2 = d1;//拷贝构造2
	return 0;
}
  1. 若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储按字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。

注意:类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。

四、赋值运算符重载:

1.运算符重载:

C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)

注意:

  1. 重载操作符必须有一个类类型参数。
  2. 用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义。
  3. 不能通过连接其他符号来创建新的操作符:比如operator@。
  4. 作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this。
  5. .* :: sizeof ? : . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
class Date
{
    
    
public:
	Date(int year = 2022, int month = 8, int day = 17)//构造函数完成类的初始化
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator==(const Date date)
	{
    
    
		return _year == date._year
			&& _month == date._month
			&& _day == date._day;
	}
	bool operator!=(const Date date)
	{
    
    
		return !(*this == date);
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1(2022, 8, 1);//调用无参构造
	Date d2(2022, 8, 2);
	d1 == d2;
	return 0;
}

2.赋值运算符重载:

格式:

  1. 单个形参,该形参是对本类类型对象的引用。
  2. 返回值类型为引用,可以提高返回的效率,有返回值目的是为了支持连续赋值。
  3. 检测是否自己给自己赋值。
  4. 返回*this :要符合连续赋值的含义。
class Date
{
    
    
public:
	Date(int year = 2022, int month = 8, int day = 17)//构造函数完成类的初始化
	{
    
    
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator==(const Date date)
	{
    
    
		return _year == date._year
			&& _month == date._month
			&& _day == date._day;
	}
	bool operator!=(const Date date)
	{
    
    
		return !(*this == date);
	}
	Date& operator=(const Date& date)
	{
    
    
		if (*this != date)
		{
    
    
			_year = date._year;
			_month = date._month;
			_day = date._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
    
    
	Date d1(2022, 8, 1);//调用无参构造
	Date d2(2022, 8, 2);
	d1 == d2;
	return 0;
}

特点:

  1. 用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。
  2. 赋值运算符只能重载成类的成员函数不能重载成全局函数。

注意:赋值运算符容易和拷贝构造弄混

int main()
{
    
    
	Date d1(2022, 8, 1);//调用无参构造
	Date d2(d1);//拷贝构造写法1
	Date d2 = d1;//拷贝构造写法2
	d2 = d1;//赋值运算
	return 0;
}

五、const成员:

将const修饰的“成员函数”称之为const成员函数,const实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
在这里插入图片描述
在这里插入图片描述

六、取地址及const取地址操作符重载:

这两个默认成员函数一般不用重新定义 ,编译器默认会生成。

class Date
{
    
    
public:
	Date* operator&()
	{
    
    
		return this;
	}
	const Date* operator&()const
	{
    
    
		return this;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

这两个运算符一般不需要重载,使用编译器生成的默认取地址的重载即可,只有特殊情况,才需要重载,比如想让别人获取到指定的内容!


总结

以上就是今天讲的类和对象中篇内容,包含类的6大默认成员函数,希望对刚刚阅读本篇博客的你有所帮助!

猜你喜欢

转载自blog.csdn.net/weixin_61661271/article/details/126512195