【从小白到大白02】c++类和对象-中

image-20221012214239017

赋值运算符重载

这里贴一份Date实现代码后续要用到噢

#include<iostream>
using namespace std;
class Date
{
public:

	int GetTureDay(int year, int month)
	{
		static	int monthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
		if (month == 2 && ((year % 4 == 0 && year & 100 != 0) || (year % 400 == 0)))
		{
			return 29;
		}
		else
		{
			return monthArray[month];
		}
	}

	Date(int year = 1, int month = 1, int day = 1)//构造函数-自动调用
	{
		_year = year;
		_month = month;
		_day = day;
	}
	~Date()//析构函数
	{
		_year = 0;
		_month = 0;
		_day = 0;
	}

	bool operator==(const Date& d)//判断
	{
		return _year == d._year
			&& _month == d._month
			&& _day == d._day;
	}
	bool operator>(const Date& d)//判断大于
	{
		if (_year > d._year)
		{
			return true;
		}
		else if (_year == d._year && _month > d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day > d._day)
		{
			return true;
		}
		return false;
	}
	bool operator>=(const Date& d)//判断大于等于
	{
		return *this > d || *this == d;
	}
	bool operator<(const Date& d)//判断小于
	{
		return !((*this > d)&& (*this ==d));
	}
	bool operator<=(const Date& d) //判断小于等于
	{
		return !(*this > d);
	}
		void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	Date& operator+=(int day)
	{
		_day += day;
		while (_day > GetTureDay(_year, _month))
		{
			_day -= GetTureDay(_year, _month);
			_month++;
			if (_month == 13)
			{
				_year++;
				_month = 1;
			}
		}
		return *this;
	}
	Date operator+(int day)
	{
		Date ret(*this);
		ret += day;
		return ret;
	}
	//d1=d2
	Date& operator=(const Date& d)
//赋值重载-引用返回自定义类型 -可以连续赋值
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		else
		{
			return *this;//自己赋值自己
		}
	}
	
private:
	int _year;
	int _month;
	int _day;
};

这里贴一份栈和队列的代码后续要用到噢!

class Stack
{
public:
	Stack(int capacity = 4)
	{
		cout << "Stack(int capacity = )" <<capacity<<endl;

		_a = (int*)malloc(sizeof(int)*capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}

		_top = 0;
		_capacity = capacity;
	}
	
	// st2(st1)
	Stack(const Stack& st)
	{
		cout << "Stack(const Stack& st)" << endl;

		_a = (int*)malloc(sizeof(int)*st._capacity);
		if (_a == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		memcpy(_a, st._a, sizeof(int)*st._top);
		_top = st._top;
		_capacity = st._capacity;
	}

	 //st1 = st2;
	 //st1 = st1;
	Stack& operator=(const Stack& st)//赋值重载
	{
		//cout << "	Stack& operator=(const Stack& st)" << endl;
		if (this != &st)
		{
			free(_a);
			_a = (int*)malloc(sizeof(int)*st._capacity);
			if (_a == nullptr)
			{
				perror("malloc fail");
				exit(-1);
			}
			memcpy(_a, st._a, sizeof(int)*st._top);
			_top = st._top;
			_capacity = st._capacity;
			}

		return *this;
	}

	~Stack()
	{
		cout << "~Stack()" << endl;

		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}

	void Push(int x)
	{
		// ....
		// 扩容
		_a[_top++] = x;
	}

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

class MyQueue {
public:
	void push(int x)
	{
		_pushST.Push(x);
	}
private:
	Stack _pushST;
	Stack _popST;
	size_t _size = 0;
};

赋值运算符重载格式

形式: operator=

1.参数类型:const 参数&,传递引用可以提高传参效率

2.返回值类型:参数&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值

3.检测是否自己给自己赋值

4.返回*this :要复合连续赋值的含义

5.用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝(浅拷贝)

6。赋值运算符只能重载成类的成员函数不能重载成全局函数(全局函数时就没有this指针了,需要两个参数)-至于为什么看下一条

7.赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员函数。

注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值(比如:队列queue、栈stack)。

image-20221009234016659

下面我们对栈类型把st2赋值重载给st1(浅拷贝)

image-20221010151027819

image-20221010150938929

image-20221010000603405

若要解决以上问题,应该怎么解决呢?

以上有三种情况:

第一种: 1)st1空间小于st2 2) st1空间远小于st2 3)st1空间大于st2

如果是第一种,我们就最好使用realloc扩容,但其他两种对于使用realloc则不是最优

则我们可以选择先把原先的st1空间free掉,然后再把st2赋值重载给st1

image-20221010151735382

image-20221010151511272

那么问题就可以得到解决拉!

但如果我们向上面那样把st1赋值重载给st1呢??

image-20221010152022909

那么我们在赋值的时候就需要判断一下了

image-20221010152216424

image-20221010152402292

假如我们用栈创建队列(此时栈已经写好了赋值重载函数而队列没有写赋值重载函数

image-20221010153616288

总结:

**一般情况下不需要写赋值重载函数(使用默认的赋值重载函数就好):内置类型、 会调用自定义类型函数(如:stack)的 自定义类型(queue):会调用其自定义类型函数的赋值重载函数(stack) **

需要写赋值重载函数:需要调用析构函数的函数、涉及到资源管理的函数

前置–(++)和后置–(++)重载

前置–(++):(需要改变参数本身)引用返回对象(提高效率)

后置–(++)(不需要改变参数本身)返回值;

且函数参数要携带(int)-这是规定!

作用:1.为了区分前置重载和后置重载

2.【这样前置–(++)和后置–(++)能构成函数重载】

image-20221012170654919

那么我们就可以试一下+=和-=

image-20221012171228940

流插入(cout)和流提取(cin)重载

cout是ostream类里的对象(库里面);cin是istream类里的对象(库里面)-本身能直接调用

但是是针对内置类型(如int 、char、double等)才是直接调用;【原因:内置类型在库函数里转化成相应指令,而自定义类型比较复杂,难以实现。一句话总结:内置类型是天生的(比如吃饭,睡觉,打豆豆);而自定义类型是后天学习的(比如:唱跳rap篮球->正是需要我们去学习的地方)

对于自定义类型则不能;所以我们要用到赋值运算符(operator)对这两种重载

我们现在按照思路写一下流插入(流提取也类似):

image-20221012190607577

那为什么cout<<d1这种形式不可以呢?

因为有个隐藏的this指针在out参数之前(左边),这里默认流插入顺序:参数是从左到右所以不行捏!

如果想要类似那样的写法只能这样(看下图)这样就没有了可读性了!

image-20221012191101673

那我们不写在成员函数里面(写在外面),就不会受到隐藏this指针了呀(流插入函数另起两个参数,左边是流插入类,第二个是Date类,这样就符合流插入从左到右的参数顺序了(如下图)

image-20221012191828795

但是我们可以看到在Date成员函数外面不能调用函数的私有成员(_year,);现在我们先把它换公有

然后我们会发现一样会报错!

原因是:写在.h文件里面的全局函数或者全局变量(此时两个及两个以上的文件(如:.cpp)包含了.h文件)在.h在编译期间会同时在Date.cpp 和Test.cpp展开,在形成Test.O 和Date.o进入符号表重复出现,那么就是重定义拉!

解决办法有两个:

用static修饰函数:static修饰过的全局函数或全局变量只存在本身文件(不链接)-只出现在.h文件

image-20221012193125659

另一个是

声明和定义分离

image-20221012193619873

但是并不支持链式调用,则我们需要再改进

image-20221012194010402

但是我建议用内联函数:在编译期间会直接展开在.h文件,不会进到符号表(不链接)

,那我们为了一个流插入函数就把私有成员换公有值得吗???-不值得!!!

这里我们有两种方法:

友员中的友员声明(在任意位置都可以)

image-20221012194816890

另一种是在成员函数里另写一个函数(getDateday-类似gettureday)

const成员

将const修饰的成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。

image-20221012202738446

但因为this指针是隐藏的,所以我们不能在this指针前加const,所以我们引出了const成员函数

image-20221012202445426

const成员函数既能接收可读可写参数也能接收可读不可写参数

取地址及const取地址操作符重载

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

一般我们不用写地址操作符重载,除非我们不想让别人找到地址!如下图:

image-20221012203726350

构造函数:初始化列表

在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化。

构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值

1.初始化列表格式:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。

2.每个成员都必须通过初始化列表,就算没有写也通过,则由随机值初始化

3.我们知道在成员函数处赋值-给缺省值-是在初始化列表的时候用的;若初始化列表没有对该成员初始化则由缺省值来初始化;而在初始化列表给成员初始化后则缺省值不再使用;顺序是成员先通过初始化列表再到缺省值

4.内置类型:初始化列表给值初始化就初始化,不给则轮到缺省值,给了缺省值则缺省值,不给则随机值!

5.成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

自定义类型:调用默认它的默认构造函数,没有则报错

所以我们会用到初始化列表

image-20221012205255681

那为什么我们要用初始化列表呢?

第一种情况:

对于const成员

const成员必须在函数定义时初始化,而进入函数体内(构造函数)前就已经定义,进入构造函数后初始化叫赋值!

那么对象每个成员在什么时候定义呢?-在初始化列表!(对象整体在调用对象函数的函数里定义)

image-20221012210112590

对于没有默认构造函数的自定义类型成员(初始化时调用成员的函数的默认构造函数-没有时则需要初始化列表)

Queue的pushST需要用到stack的默认构造函数,但是stack没有默认构造函数,则需要再Queue的初始化列表初始化它!

image-20221012212826533

对于引用成员变量(只有一次初始化-在定义的地方-之后不会改变)

如果以上内容对你有帮助的化,给我点个小小的赞吧(制作不易,博主流泪~~~)

  • 14
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值