文章目录
赋值运算符重载
这里贴一份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)。
下面我们对栈类型把st2赋值重载给st1(浅拷贝)
若要解决以上问题,应该怎么解决呢?
以上有三种情况:
第一种: 1)st1空间小于st2 2) st1空间远小于st2 3)st1空间大于st2
如果是第一种,我们就最好使用realloc扩容,但其他两种对于使用realloc则不是最优
则我们可以选择先把原先的st1空间free掉,然后再把st2赋值重载给st1
那么问题就可以得到解决拉!
但如果我们向上面那样把st1赋值重载给st1呢??
那么我们在赋值的时候就需要判断一下了
假如我们用栈创建队列(此时栈已经写好了赋值重载函数而队列没有写赋值重载函数)
总结:
**一般情况下不需要写赋值重载函数(使用默认的赋值重载函数就好):内置类型、 会调用自定义类型函数(如:stack)的 自定义类型(queue):会调用其自定义类型函数的赋值重载函数(stack) **
需要写赋值重载函数:需要调用析构函数的函数、涉及到资源管理的函数
前置–(++)和后置–(++)重载
前置–(++):(需要改变参数本身)引用返回对象(提高效率)
后置–(++)(不需要改变参数本身)返回值;
且函数参数要携带(int)-这是规定!
作用:1.为了区分前置重载和后置重载
2.【这样前置–(++)和后置–(++)能构成函数重载】
那么我们就可以试一下+=和-=
流插入(cout)和流提取(cin)重载
cout是ostream类里的对象(库里面);cin是istream类里的对象(库里面)-本身能直接调用
但是是针对内置类型(如int 、char、double等)才是直接调用;【原因:内置类型在库函数里转化成相应指令,而自定义类型比较复杂,难以实现。一句话总结:内置类型是天生的(比如吃饭,睡觉,打豆豆);而自定义类型是后天学习的(比如:唱跳rap篮球->正是需要我们去学习的地方)
对于自定义类型则不能;所以我们要用到赋值运算符(operator)对这两种重载
我们现在按照思路写一下流插入(流提取也类似):
那为什么cout<<d1这种形式不可以呢?
因为有个隐藏的this指针在out参数之前(左边),这里默认流插入顺序:参数是从左到右所以不行捏!
如果想要类似那样的写法只能这样(看下图)这样就没有了可读性了!
那我们不写在成员函数里面(写在外面),就不会受到隐藏this指针了呀(流插入函数另起两个参数,左边是流插入类,第二个是Date类,这样就符合流插入从左到右的参数顺序了(如下图)
但是我们可以看到在Date成员函数外面不能调用函数的私有成员(_year,);现在我们先把它换公有
然后我们会发现一样会报错!
原因是:写在.h文件里面的全局函数或者全局变量(此时两个及两个以上的文件(如:.cpp)包含了.h文件)在.h在编译期间会同时在Date.cpp 和Test.cpp展开,在形成Test.O 和Date.o进入符号表重复出现,那么就是重定义拉!
解决办法有两个:
用static修饰函数:static修饰过的全局函数或全局变量只存在本身文件(不链接)-只出现在.h文件
另一个是
声明和定义分离
但是并不支持链式调用,则我们需要再改进
但是我建议用内联函数:在编译期间会直接展开在.h文件,不会进到符号表(不链接)
,那我们为了一个流插入函数就把私有成员换公有值得吗???-不值得!!!
这里我们有两种方法:
友员中的友员声明(在任意位置都可以)
另一种是在成员函数里另写一个函数(getDateday-类似gettureday)
const成员
将const修饰的成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
但因为this指针是隐藏的,所以我们不能在this指针前加const,所以我们引出了const成员函数
const成员函数既能接收可读可写参数也能接收可读不可写参数
取地址及const取地址操作符重载
这两个默认成员函数一般不用重新定义 ,编译器默认会生成!
一般我们不用写地址操作符重载,除非我们不想让别人找到地址!如下图:
构造函数:初始化列表
在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象中成员变量的初始化。
构造函数体中的语句只能将其称为赋初值,而不能称作初始化。因为初始化只能初始化一次,而构造函数体内可以多次赋值。
1.初始化列表格式:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟一个放在括号中的初始值或表达式。
2.每个成员都必须通过初始化列表,就算没有写也通过,则由随机值初始化
3.我们知道在成员函数处赋值-给缺省值-是在初始化列表的时候用的;若初始化列表没有对该成员初始化则由缺省值来初始化;而在初始化列表给成员初始化后则缺省值不再使用;顺序是成员先通过初始化列表再到缺省值
4.内置类型:初始化列表给值初始化就初始化,不给则轮到缺省值,给了缺省值则缺省值,不给则随机值!
5.成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关
自定义类型:调用默认它的默认构造函数,没有则报错
所以我们会用到初始化列表
那为什么我们要用初始化列表呢?
第一种情况:
对于const成员
const成员必须在函数定义时初始化,而进入函数体内(构造函数)前就已经定义,进入构造函数后初始化叫赋值!
那么对象每个成员在什么时候定义呢?-在初始化列表!(对象整体在调用对象函数的函数里定义)
对于没有默认构造函数的自定义类型成员(初始化时调用成员的函数的默认构造函数-没有时则需要初始化列表)
Queue的pushST需要用到stack的默认构造函数,但是stack没有默认构造函数,则需要再Queue的初始化列表初始化它!