类和对象(中)

目录

一、构造函数

1.概念

2.特性

二、拷贝构造函数

1.概念

2.特性

3.注意事项

​编辑

三、赋值运算符重载

1.运算符重载

(1)概念

(2)定义和使用

(3)注意事项

2.赋值运算符重载

(1)概念

(2) 赋值运算符重载格式

(3)注意事项


一、构造函数

1.概念

 构造函数:属于六大特殊成员函数,名字与域名相同,创建类型对象时有编译器自动调用,以保证每个数据成员都有一个合适的初值。构造函数在类型对象生命周期类仅仅调用一次(不能反复初始化)

#include
using namespace std;

class Date
{
public:
	//1. 函数名与类名相同,无参调用
	Date()
	{
		_year = 1;
		_month=1;
		_day=1;
	}

	//2.函数名与类名相同,带参调用
	Date(int year,int month,int day)
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}

	void Display()
	{
		cout<< _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1();//调用 1

	Date d2(2022,8,5);//调用 2


	return 0;
}

1.和2.可以结合来写
定义了一个类--Date
class Date
{
	公有
public:
	void SetDate(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Display()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	私有
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;//实例化出一个对象,d1
	d1.SetDate(2018, 5, 1);//手动初始化第一次
	d1.Display();

	Date d2;//实例化出一个对象,d2
	d2.SetDate(2018, 7, 1);//手动初始化第二次
	d2.Display();
	return 0;
}

2.特性

注意:

1.构造函数其实“名不符实”。构造函数的主要任务是初始化对象而非开空间创建对象

2.任何一个类的默认构造函数有三个:全缺省,无参,编译器自动生成,但是三个只能有一个存在。

3.任何一个类的默认构造参数都是可以不用参数就直接调用。

4.不能通过对象调用构造函数,(这样是错误的: d1.Date() )

二、拷贝构造函数

1.概念

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

2.特性

拷贝构造函数也是特殊的成员函数,其 特征 如下:
1. 拷贝构造函数 是构造函数的一个重载形式
2. 拷贝构造函数的 参数只有一个 必须是类类型对象的引用 ,使用 传值方式编译器直接报错 ,因为会引发无穷递归调用。
定义
#include
using namespace std;

class Date
{
public:
	// 构造会频繁调用,所以直接放在类里面定义作为inline
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//拷贝构造函数
	Date(const Date& d)//必须传引用
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2022, 8, 21);
	Date d2(10);
	Date d3(d1);
	return 0;
}
类和对象(中)_第1张图片

3.注意事项

1.拷贝构造函数必须使用应用传参,不然在Date d2 (d1)会出现无穷递归(反复调用拷贝构造函数)

2.使用浅拷贝时候要注意系统会不会析构两次

类和对象(中)_第2张图片

 

关于两次析构可以通过这个错误案例来观察


//栈
class Stack
{
public:
	//构造
	Stack(int capacity = 4)
	{
		_a = (int*)malloc(sizeof(int));
		if (_a == nullptr)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}
		_top = 0;
		_capacity = capacity;
	}
	//拷贝构造
	//...使用系统默认生成的
private:
	int* _a;
	int _top;
	int _capacity;
};
int main()
{
	Stack st1(5);
	Stack st2(st1);

	return 0;
}

类和对象(中)_第3张图片

出现上面这个错误提示的原因是 st1,st2指向了同一块空间,会造成析构了两次 

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

三、赋值运算符重载

类和对象(中)_第4张图片

 

1.运算符重载

(1)概念

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

(2)定义和使用

函数原型:返回值类型 operator操作符(参数列表)

// 全局的operator==
class Date
{ 
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 } 
//private:
 int _year;
 int _month;
 int _day;
};
这里会发现运算符重载成全局的就需要成员变量是公有的,那么问题来了,封装性如何保证?
这里其实可以用我们后面学习的友元解决,或者干脆重载成成员函数。

(3)注意事项

1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型参数
3.用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不能改变其含义
4.作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
5..* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。

2.赋值运算符重载

(1)概念

赋值运算符重载是对两个已经存在的自定义类型对象的赋值

(2) 赋值运算符重载格式

参数类型:const T&,传递引用可以提高传参效率
返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
检测是否自己给自己赋值
返回*this :要复合连续赋值的含义
class Date
{ 
public :
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 
 Date (const Date& d)
 {
 _year = d._year;
 _month = d._month;
 _day = d._day;
 }
Date& operator=(const Date& d)
 {
 if(this != &d)
 {
 _year = d._year;
 _month = d._month;
 _day = d._day;
 }

 return *this;
 }
private:
 int _year ;
 int _month ;
 int _day ;
};

(3)注意事项

1.赋值运算符只能重载成类的成员函数不能重载成全局函数
class Date
{
public:
 Date(int year = 1900, int month = 1, int day = 1)
 {
 _year = year;
 _month = month;
 _day = day;
 }
 int _year;
 int _month;
 int _day;
};
// 赋值运算符重载成全局函数,注意重载成全局函数时没有this指针了,需要给两个参数
Date& operator=(Date& left, const Date& right) {
 if (&left != &right)
 {
 left._year = right._year;
 left._month = right._month;
 left._day = right._day;
 }
 return left; }
// 编译失败:
// error C2801: “operator =”必须是非静态成员
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的 赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的 成员函数。
2.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
class Time
{
public:
 Time()
 {
 _hour = 1;
 _minute = 1;
 _second = 1;
 }
 Time& operator=(const Time& t)
 {
 if (this != &t)
 {
 _hour = t._hour;
 _minute = t._minute;
 _second = t._second;
 }
 return *this;
 }
private:
 int _hour;
 int _minute;
 int _second;
};
class Date
{
private:
 // 基本类型(内置类型)
 int _year = 1970;
 int _month = 1;
 int _day = 1;
 // 自定义类型
 Time _t;
};
int main()
{
 Date d1;
 Date d2;
 d1 = d2;
 return 0; 
}
注意:如果类中未涉及到资源管理,赋值运算符是否实现都可以;一旦涉及到资源管理则必须要实现。
类和对象(中)_第5张图片

 

你可能感兴趣的:(c++,c++)