0
点赞
收藏
分享

微信扫一扫

陪玩小程序都需要怎么做?

先峰老师 2024-06-01 阅读 9

目录

1.类的6个默认成员函数 

2. 构造函数

2.1 构造函数的概念

2.2 构造函数的特性

3. 析构函数

3.1 析构函数的概念

3.2 析构函数的特性

4.拷贝构造函数

4.1 拷贝构造函数的概念

4.2 拷贝构造函数的特性

5.赋值运算符重载函数

5.1运算符重载函数

5.2 赋值运算符重载函数

 5.3 前置++和后置++重载

前置++:

后置++:

6.(&)取地址操作符重载函数和const取地址操作符重载函数


1.类的6个默认成员函数 

如果一个类中什么成员都没有,简称为空类。可是空类中真的什么都没有吗?

其实并不是的,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。 默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

10cc6ae830cc460293c48d89a2fc6c29.png

2. 构造函数

2.1 构造函数的概念

如下代码,我们定义一个日期类并且调用成员函数:

#include <iostream>
using namespace std;

class Data
{
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()
{
	Data d1;
	d1.Init(2023, 5, 23);
	d1.Print();

	Data d2;
	d2.Init(2022, 5, 23);
	d2.Print();
	return 0;
}

 44e1ffcedc9c467aadcdcaf42e7d37aa.png

按照我们之前学过的,按部就班地先调用初始化成员函数,再调用打印成员函数,运行结果也中规中矩地跑出来了。

可是有一天,我需要很多个Data变量,写代码又太急躁,在创建某个Data变量时忘记调用Init成员函数了,如下所示:

#include <iostream>
using namespace std;

class Data
{
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()
{
	Data d1;
	d1.Init(2023, 5, 23);
	d1.Print();

	Data d2;
	d2.Print();

	return 0;
}

结果d2出现了随机值:

1e18b406df5f416aaf074c75e432021d.png

答案当然是有的,这就引出了C++的1个默认成员函数——构造函数:

2.2 构造函数的特性

其特征如下:

验证如下:

#include <iostream>
using namespace std;

class Data
{
public:

	//退出历史舞台:
	/*void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}*/

	//1. 函数名与类名相同;2. 无返回值;
	Data(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	//4. 构造函数可以重载;
	Data()
	{
		_year = 8;
		_month = 8;
		_day = 8;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
    //调用含参构造函数:
	Data d1(2023, 5, 23); //3. 对象实例化时编译器自动调用对应的构造函数;
	d1.Print();

    //调用无参构造函数:
	Data d2;   //注意这里不能用诸如:Data d2();不能加(),因为会与函数声明产生歧义;
	d2.Print();
	return 0;
}

0ec1ea8c46d54512a7b8f4b376ae0b7b.png

当然这里也完全可以用到缺省参数:

#include <iostream>
using namespace std;

class Data
{
public:

	Data(int year = 8, int month = 8, int day = 8)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Data d1(2023, 5, 23); 
	d1.Print();

	Data d2(2023, 5);
	d2.Print();

	Data d3;
	d3.Print();
	return 0;
}

27c42a08303444c2a7c7c03cd85b2fb5.png

代码验证如下:

#include <iostream>
using namespace std;

class Data
{
public:

	如果显示定义,编译器将不再生成
	//Data(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()
{
	Data d1;
	d1.Print();
	return 0;
}

将Date类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数:

a623d63f50a747b791ab574543849818.png

将Date类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成无参构造函数,放开后报错:error C2512: “Date”: 没有合适的默认构造函数可用:

830c8ed1f42a4d059858ba4bc84f8d85.png


这时你可能要问了:

在不显示定义构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数似乎并没有什么用处呀!?

上面d1对象调用了编译器生成的默认构造函数,但是d1的对象_year/_month/_day,结果显示依旧是随机值,上面的运行结果就是铁铮铮的事实呀!这不是恰恰证明了这里编译器生成的默认构造函数并没有什么卵用吗?

这就涉及到了构造函数的第6个特性:

代码验证如下:

#include <iostream>
using namespace std;

class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
 public:
void Print()
{
	cout << _year << "-" << _month << "-" << _day << endl;
}
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	d.Print();
	return 0;
}

2c14a15d9be9457d8eddcce7c59bddbb.png

说到这里,我又有些不解,同样都是变量,为什么还要分自定义类型调用它的默认成员函数,内置类型却不做处理呢?这难道不是一件画蛇添足的事情吗?

代码验证如下:

#include <iostream>
using namespace std;
class Time
{
public:
	Time()
	{
		cout << "Time()" << endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	// 基本类型(内置类型)
    // C++11支持,声明时给缺省值
	int _year = 2023;
	int _month = 5;
	int _day = 23;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	d.Print();
	return 0;
}

3120721a56154ceaa8705d2cb793f407.png


思考如下代码能否正常运行:

#include <iostream>
using namespace std;
class Date
{
public:
	Date()
	{
		_year = 2023;
		_month = 5;
		_day = 23;
	}
	Date(int year = 2023, int month = 5, int day = 23)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
// 以下测试函数能正常运行吗?
void Test()
{
	Date d1;
}

答案是否定的:

d656dc64cf62454db57c305c43dbf8e2.png

针对以上现象,可以引出构造函数的第7个特性:

3. 析构函数

3.1 析构函数的概念

通过上面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没的呢?这就需要我们学习析构函数了:

3.2 析构函数的特性

析构函数也是特殊的成员函数,其特征如下:

#include <iostream>
using namespace std;

class Data
{
public:
	Data(int year = 2023, int month = 5, int day = 23)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Ptint()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
    
    //1. 析构函数名是在类名前加上字符 ~;2. 无参数无返回值类型;
	~Data()
	{
		cout << "~Data" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Data d;
	d.Ptint();
    //这里为调用~Data,4. 对象生命周期结束时,C++编译系统自动调用析构函数;
	return 0;
}

8a52bdecb899413c8864f56c085965be.png

当然,Data类并不需要析构函数,这里只是为了证明C++自动调用了析构函数。

我们将析构函数用到顺序表中,可能会对析构函数有更深刻的理解:

#include <iostream>
using namespace std;

typedef int DataType;

class SeqList
{
public:
	SeqList()
	{
		cout << "已经调用了SeqList()构造函数;" << endl;
		_a = (DataType*)malloc(sizeof(DataType) * 4);
		if (_a == nullptr)
		{
			perror("malloc failed");//如果扩容失败,说明原因
			exit(-1);
		}
		_size = 0;//当size≥capacity时就动态开辟空间
		_capacity = 4;//初始化数组容量为4
	}

	~SeqList()
	{
		cout << "已经调用了~SeqList()析构函数;" << endl;
		free(_a);
		_a = nullptr;
		_size = _capacity = 0;
	}

private:
	int* _a;
	int _size;
	int _capacity;
};

int main()
{
	SeqList sl;
	return 0;
}

5bc4ac37324a40f48897bdce2e1c6215.png


对于第3条特性,系统自动生成默认的析构函数,会不会完成一些事情呢?

代码验证如下:

#include <iostream>
using namespace std;

class Time
{
public:
	~Time()
	{
		cout << "已经调用了~Time()析构函数" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year = 2023;
	int _month = 5;
	int _day = 23;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	return 0;
}

运行结果为:

c3aa8ce8ea1245e68bad3d05be46c610.png

对以上结果和第3、第5条特性的详细解释:

4.拷贝构造函数

4.1 拷贝构造函数的概念

电视剧中以及现实中,双胞胎的例子不在少数,我们甚至可以说简直他们就是一个模子里刻出来的!那么,在创建对象时,可否创建一个与已存在对象一某一样的新对象呢?答案的肯定的。

4.2 拷贝构造函数的特性

代码验证如下:

不考虑特性2,我们偏偏就要直接传值调用:

#include <iostream>
using namespace std;
class Date
{
public:
    Date(int year = 2023, int month = 7, int day = 7)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    Date( Date d)   
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }

    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    Date d1;
    d1.Print();
    Date d2(d1);
    d2.Print();
    return 0;
}

会发现程序报错:

b31d40394d984383bd2ed4c7383d1dc6.png

这是为什么呢?答案就像特征2中所说的,在此过程中引发了无穷递归调用:

当我们直接传值调用时,会发生先传值再调用拷贝构造函数的情况,即:

1a006970791e4df69d80e6f568086dfa.png

所以正确应该如特性2所说的那样:

#include <iostream>
using namespace std;
class Date
{
public:
    Date(int year = 2023, int month = 7, int day = 7)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    // Date( Date d)   // 错误写法:编译报错,会引发无穷递归
    Date( Date& d)   // 正确写法
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }

    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    Date d1;
    d1.Print();
    Date d2(d1);
    d2.Print();
    return 0;
}

c645327e9ba447eabf769d8e8016f1ca.png

那么在概念中又提到:(一般常用const修饰),这是为什么呢?

这是为了防止我们在定义拷贝构造函数时写反了:

Date( Date& d)   
    {
        d._year = _year;
        d._month = _month;
        d._day = _day;
    }

那么运行结果不但不会正确,反而会偷鸡不成蚀把米:

e2ca2a2a701447fca1d4aece104a34ee.png

所以加上const,即使出现了这样的低级错误,编译器就会报错,我们也能及时发现:

 Date( const Date& d)   
    {
        d._year = _year;
        d._month = _month;
        d._day = _day;
    }

869fcebb101943eab5ebba1c98d195c5.png

正确代码:

#include <iostream>
using namespace std;
class Date
{
public:
    Date(int year = 2023, int month = 7, int day = 7)
    {
        _year = year;
        _month = month;
        _day = day;
    }
    // Date( Date d)   // 错误写法:编译报错,会引发无穷递归
    Date( const Date& d)   // 正确写法
    {
        _year = d._year;
        _month = d._month;
        _day = d._day;
    }

    void Print()
    {
        cout << _year << "-" << _month << "-" << _day << endl;
    }

private:
    int _year;
    int _month;
    int _day;
};
int main()
{
    Date d1;
    d1.Print();
    Date d2(d1);
    d2.Print();
    return 0;
}

代码验证如下:

#include <iostream>
using namespace std;
class Time
{
public:
	Time()
	{
		_hour = 8;
		_minute = 8;
		_second = 8;
	}
	Time(const Time& t)
	{
		_hour = t._hour;
		_minute = t._minute;
		_second = t._second;
		cout << "已经调用了!Time::Time(const Time&)" << endl;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	// 基本类型(内置类型)
	int _year = 2023;
	int _month = 7;
	int _day = 7;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d1;
	// 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数
	// 但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数
	Date d2(d1);
	d2.Print();
	return 0;
}

66d29c0c52404b548d981f17b4d16f1d.png


与前面的构造函数和析构函数相似的问题:编译器生成的默认拷贝构造函数已经可以完成字节序的值拷贝了,还需要自己显式实现吗?

当然像上面的Data类这样的类是没必要的。那么像顺序表之类的类呢?验证如下:

#include <iostream>
using namespace std;

typedef int DataType;

class SeqList
{
public:
	SeqList()
	{
		cout << "已经调用了SeqList()构造函数;" << endl;
		_a = (DataType*)malloc(sizeof(DataType) * 4);
		if (_a == nullptr)
		{
			perror("malloc failed");//如果扩容失败,说明原因
			exit(-1);
		}
		_size = 0;//当size≥capacity时就动态开辟空间
		_capacity = 4;//初始化数组容量为4
	}

	//打印
	void Print()
	{
		for (int i = 0; i < _size; i++)
		{
			cout << _a[i] << endl;
		}
	}

	//尾插
	void PushBack(const DataType& x)
	{
		_a[_size] = x;
		_size++;
	}

	~SeqList()
	{
		cout << "已经调用了~SeqList()析构函数;" << endl;
		free(_a);
		_a = nullptr;
		_size = _capacity = 0;
	}

private:
	int* _a;
	int _size;
	int _capacity;
};

int main()
{
	SeqList sl1;
	sl1.PushBack(1);
	sl1.PushBack(2);
	sl1.PushBack(3);
	sl1.PushBack(4);
	sl1.Print();

	SeqList sl2(sl1);
	sl2.Print();
	return 0;
}

运行结果如图:

7e3a76d24fa24a6eb89a9fd691544b0c.png

我们可以看到,程序崩溃了!这是为什么呢?

打开监视窗口看一下sl1和sl2的内存地址:

a665e2e2d22f42d2910c10bb75c0a469.png

发现二者的地址相同,所以我们就知道了: 


现在我已经知道原因了,那么正确的代码应该怎么写呢?这就需要用到深拷贝去解决(关于深拷贝后面会有详解):

//自定义拷贝构造函数,不用编译器默认生成的(深拷贝)
	SeqList( const SeqList& sl)
	{
		_a = (DataType*)malloc(sizeof(DataType) * 4);//我也开辟一个空间
		if (_a == nullptr)
		{
			perror("malloc failed");//如果扩容失败,说明原因
			exit(-1);
		}
		memcpy(_a, sl._a, sizeof(int) * sl._capacity);
		_size = sl._size;
		_capacity = sl._capacity;
	}

32d8e2931faa4aba9c7fd30cce45486b.png

所以,我们应该要明白:

5.赋值运算符重载函数

5.1运算符重载函数

在学习赋值运算符重载之前,我们先来了解一下运算符重载:

通过上面的学习,我们已经知道了内置类型和自定义类型的区别,思考这样一个问题:

那么这时候就需要我们自己写一个函数来实现:

写一个大于比较函数:

bool Greater(Data d1, Data d2)
{
	if (d1._year > d2._year)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month > d2._month)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month ==d2._month && d1._day > d2._day)
	{
		return true;
	}
		return false;
}

再比如我写一个等于比较的函数:

bool Equal(Data d1, Data d2)
{
	return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}

运行一下:

#include <iostream>
using namespace std;
class Data
{
public:
	Data(int year = 2012, int month = 7, int day = 7)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Data(const Data& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	void Ptint()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

//private:
	int _year;
	int _month;
	int _day;
};

//布尔类型(bool)用于表示真(true)和假(false)的值。
//它只有两个取值:true 和 false,分别对应 1 和 0 。
bool Greater(Data d1, Data d2)
{
	if (d1._year > d2._year)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month > d2._month)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month ==d2._month && d1._day > d2._day)
	{
		return true;
	}
		return false;
}

bool Equal(Data d1, Data d2)
{
	return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}

int main()
{
	Data d1(2013, 7, 7);
	Data d2(2012, 7, 7);
	cout << Greater(d1, d2) << endl;
	cout << Equal(d1, d2) << endl; 
	return 0;
}

40c3044b923f4d26a6af88b477fb7d38.png

说到这里,我们来说一个题外话,关于函数的命名,其实在C语言中我们就遇到过很多了,一个函数命名就如同给自己的孩子取名字一样,比如上面的判断大于和判断相等函数,我能用Greater、Equal,为什么就不能用DaYu、DengYu,或者Compare1、Compare2,又或者func1、func2呢?

这些确实都是可以的呀!我创建的函数,我乐意怎样取名我就怎样取名!

可是话说回来,你的孩子在你口中叫狗蛋儿、在老师口中叫张三,有一天你去开家长会,老师问你是谁的家长,你说你是狗蛋儿的家长,你这样说老师会知道张三就是狗蛋儿,狗蛋儿就是张三吗?

话再说回来,你写的函数叫DaYu、DengYu,而你的同事要用这个函数,你写的DaYu、DengYu,同事能知道这是个什么函数吗?

函数名字为:关键字operator后面接需要重载的运算符符号。

因此上述两个函数就可以写为(更加规范,我们加上const和&):

//bool Greater(Data d1, Data d2)
bool operator>(const Data& d1, const Data& d2)
{
	if (d1._year > d2._year)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month > d2._month)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month ==d2._month && d1._day > d2._day)
	{
		return true;
	}
		return false;
}

//bool Equal(Data d1, Data d2)
bool operator == (const Data& d1, const Data& d2)
{
	return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
}
int main()
{
	Data d1(2013, 7, 7);
	Data d2(2012, 7, 7);
	/*cout << Greater(d1, d2) << endl;
	cout << Equal(d1, d2) << endl; */
	cout << operator > (d1, d2) << endl;
	cout << operator == (d1, d2) << endl;
	return 0;
}

09a4e81684514c94a4c1a3efa58519c0.png

以及为了令自定义类型更贴合与内置类型一样让编译器自己计算,直接转为指令:

int main()
{
	Data d1(2013, 7, 7);
	Data d2(2012, 7, 7);
	/*cout << Greater(d1, d2) << endl;
	cout << Equal(d1, d2) << endl; */
	/*cout << operator > (d1, d2) << endl;
	cout << operator == (d1, d2) << endl;*/

	bool ret1 = d1 > d2;   //d1>d2吗?是为1,否为0;
	bool ret2 = d1 == d2;  //d1=d2吗?是为1,否为0;
	int a = 3 > 2;         //3>2吗?是为1,否为0;
	int b = 3 == 2;        //3=2吗?是为1,否为0;
	cout << ret1 << endl << ret2 << endl;
	cout << a << endl << b << endl;
	return 0;
}

6c14fcf3156e47f4be3a01e2d23d84ae.png


仔细观察我们上面写的Data类,可以发现我把private注释掉了,那现在我把注释关掉:

c95e1a2b39d84902b7952dc26cc6ed78.png

成员变量变私有了该怎么办呢?其实C++常用的解决方法是直接将函数放到类里面,因为类里面可以随便访问private:

class Data
{
public:
	Data(int year = 2012, int month = 7, int day = 7)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Data(const Data& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	
	bool operator == (const Data& d1, const Data& d2)
	{
		return d1._year == d2._year && d1._month == d2._month && d1._day == d2._day;
	}

	void Ptint()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

可是这样写发现还是编译不通过:

02d4b6013b5f4a4dad00c0501e5e4edb.png

它说函数参数太多?!我放到类外面参数就不多,怎么放到类里面就多参数了呢?

对!因为存在一个隐含的this指针(详细请看C++入门2——类与对象(1)中的3),所以这时就可以这样修改:

class Data
{
public:
	Data(int year = 2012, int month = 7, int day = 7)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Data(const Data& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	bool operator>(const Data& d2)
	{
		if (_year > d2._year)
		{
			return true;
		}
		else if (_year == d2._year && _month > d2._month)
		{
			return true;
		}
		else if (_year == d2._year && _month == d2._month && _day > d2._day)
		{
			return true;
		}
		return false;
	}

	bool operator == ( const Data& d2)
	{
		return _year == d2._year && _month == d2._month && _day == d2._day;
	}

	void Ptint()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

那么在函数调用时,编译器就会帮我们负重前行:

int main()
{
	Data d1(2013, 7, 7);
	Data d2(2012, 7, 7);

	bool ret1 = d1 > d2;    //d1.operator>(d2)--->d1.operator>(&d1,d2)
	bool ret2 = d1 == d2;   //d1.operator==(d2)--->d1.operator==(&d1,d2)
	
	cout << ret1 << endl << ret2 << endl;
	return 0;
}

所以运算符重载归纳有以下特点:

5.2 赋值运算符重载函数

铺了那么多前戏,终于来到我们要学习的赋值运算符重载函数了:

我们知道,拷贝构造就是将一个已经初始化的变量A拷贝到未初始化的变量B中,

那么如果存在两个都已经初始化的变量A、B,我想把A的值拷贝到B,显然就不能再用拷贝构造了,要用到我们就要开始讲的赋值运算符重载:

(详解精华都在代码里):

#include <iostream>
using namespace std;
class Data
{
public:
	Data(int year = 2012, int month = 7, int day = 7)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Data(const Data& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	//d1=d2;d1传给this,d2传给d
	//返回值应该是什么类型呢?当然是Data类型;应该返回d1的地址,所以用&引用返回
	Data& operator=(const Data& d)
	{
		//判断是否为自己给自己赋值,&放到这里不是引用,是取地址:
		//判断d2的地址是否与d1地址相等
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;//返回d1的地址
	}

	void Ptint()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Data d1(2023, 7, 7);
	Data d2(2022, 8, 8);
	//拷贝构造:一个已经存在的对象去拷贝初始化另一个对象
	Data d3(d2);
	d1.Ptint();
	d2.Ptint();
	d3.Ptint();
	cout << endl;
	//赋值运算符重载:两个已经存在的对象拷贝
	d1 = d2;//运算自定义类型,就要用到运算符重载operator=
	d1.Ptint();
	d2.Ptint();
	d3.Ptint();
}

bfc851358b7a4f188d2536f11d56f2a8.png


 5.3 前置++和后置++重载

在C语言中,我们已经知道了:前置++,先加后用;后置++,先用后加 这样的基本常识

那么我们学了运算符重载,现在我要自定义类型Data前置++和后置++,要怎么定义和实现呢?

前置++:

前置++为先+1后使用;所以前置++的返回值应该是返回+1之后的结果;

故前置++重载函数的实现为:

// 前置++:返回+1之后的结果
	Date& operator++()//返回d1,所以返回值类型当然为Date类型
	{
		_day += 1;
		return *this;//this指向的对象函数结束后不会销毁,故以&引用方式返回提高效率
	}

后置++:

后置++为先使用后+1;所以后置++的返回值应该为+1之前的旧值,故需在实现时需要先将this保存一份,然后给this+1;

那么问题来了,前置++和后置++都是一元运算符,实现起来两个函数名字相同,都是operator++,怎么才能区分这两个函数呢?

故后置++重载函数的实现为:

	Date operator++(int)
	{
		Date temp(*this);// 先将this保存一份,然后给this + 1
		_day += 1;
		return temp;    // temp是临时对象,出了作用域就会被销毁,
                        //因此只能以值的方式返回,不能返回引用
	}

验证:

#include <iostream>
using namespace std;
class Date
{
public:
	Date(int year = 2023, int month = 7, int day = 7)
	{
		_year = year;
		_month = month;
		_day = day;
	}
    // 前置++:返回+1之后的结果
	Date& operator++()//返回d1,所以返回值类型当然为Date类型
	{
		_day += 1;
		return *this;//this指向的对象函数结束后不会销毁,故以&引用方式返回提高效率
	}
	Date operator++(int)
	{
		Date temp(*this);// 先将this保存一份,然后给this + 1
		_day += 1;
		return temp;    // temp是临时对象,出了作用域就会被销毁,
                        //因此只能以值的方式返回,不能返回引用
	}
		void Print()
		{
			cout << _year << "-" << _month << "-" << _day << endl;
		}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	Date d2(2022, 7, 7);
	d1 = ++d2;//前置++,编译器默认调用不带(int)参数的operator++
	d1.Print();
	d1 = d2++;//后置++,编译器默认调用带(int)参数的operator++
	d1.Print();
	return 0;
}

042923162f184534aa7a272a212b0bb8.png


6.(&)取地址操作符重载函数和const取地址操作符重载函数

终于来到类的最后两个默认成员成员函数了,二者的形式为:

class Date
{
public:
	//(&)取地址操作符重载
	Date* operator&()
	{
		return this;
	}

	//const(&)取地址操作符重载
	const Date* operator&()const
	{
		return this;
	}
private:
	int _year; // 年
	int _month; // 月
	int _day; // 日
};

(本篇完) 

举报

相关推荐

0 条评论