1.赋值运算符重载
1.1运算符重载
C++为了增强可读性,让自定义类也能加减乘除比大小等运算,引入了运算符重载,运算符重载是具有特殊函数名的函数,函数参数个数是固定的。
ps:函数重载是函数名相同,参数不同的函数,系统会自动识别类型去调用相应的函数
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)以下列日期类为例
class Date//一个时间类
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{//全缺省的构造函数
_year = year;
_month = month;
_day = day;
}
bool operator==(Date d)//这个就是==重载
{
return (_year == d._year && _moth == d._moth && _day == d._day);
}
// bool operator==(Date* this,Date d)
// {//原本模样,在成员函数第一个参数是自己的指针称为this指针。
//后面下面会用到这个知识点
// return (this->_year == d._year &&
// this->_moth == d._moth && this->_day == d._day);
// }//因为this经常用所以C++直接省略了
private://私人的,不可随意访问
int _year;//函数成员
int _month;
int _day;
};
注意:
1.不能通过连接其他符号来创建新的操作符:比如operator@
2.重载操作符必须有一个类类型或者枚举类型的操作数。
(因为内置类型 像int这种,编译器已经重载好了,所以只能重载我们自定义的类型,类和枚举类型都是自定义类型)
3.用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不能改变其含义
(如果改变了就违背了,可读性就会大大下降)
4.作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的
操作符有一个默认的形参this,限定为第一个形参
(如上代码,成员函数第一个参数是自己的指针称为this指针。)
5.* 、:: 、sizeof 、?: 、. 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
1.2赋值运算符重载
赋值运算符(其实就是=运算符重载)有4个小点要注意
1. 参数类型
2. 返回值
3. 检测是否自己给自己赋值(别忘记啦O—O)
4. 返回*this(this是指向自己的指针,所以返回*this就是返回自己)
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
Date& operator=(Date& d)//Date& 这个是运用了引用可以加快效率
{//这就是赋值运算符重载
_year = d.year;
_month = d.month;
_day = d.day;
return *this;
}
private:
int _year;
int _month;
int _day;
};
5.一个类里没有去实现赋值运算符(=运算符),编译器也会自己实现一个,按照一个一个字节拷贝。
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;
};
int main()
{
Date d1;
Date d2(2018,10, 1);
// 这里d1调用的编译器生成operator=完成拷贝,d2和d1的值也是一样的。
d1 = d2;
return 0;
}
那我们为什么要去实现这个=运算符重载呢??因为解决不了部分问题,比如开辟空间和指针等就要自己去实现=运算符重载。如下程序也是这样崩溃的。
class String
{
public:
String(const char* str = "")
{
_str = (char*)malloc(strlen(str) + 1);//
strcpy(_str, str);
}
~String()
{
cout << "~String()" << endl;
free(_str);
}
private:
char* _str;
};
int main()
{
String s1("hello");
String s2("world");
s1 = s2;
}
2.const
概念:const名 常量限定符,让变量具有常属性,不能随意的改动。
修饰一般常量如下
int const a = 1;
const int a = 1;//俩种都一样
修饰指针
char a = '1';
char b = '2';
const char *p1 = a;//const指针使其内容不能改变,如下
//*p1 = '0';这个是会报错的
char const *p2 = a;//同上(以*分割,const在*左边作用一样,在*右边作用一样)
char *const p3 = a;//不能改变对象,p3内容可变,但只能指向a,如下
//*p3 = '0';可行
//p3 = b;不可行,对象不能变,哥们我纯情男大。
const char * const p4 = a;//内容和对象都不能改变
const修饰类的成员函数
将const修饰的类成员函数称之为const成员函数,const修饰类成员函数,实际修饰该成员函数隐含的this指针,表明在该成员函数中不能对类的任何成员进行修改。
这时就能给最上面的日期类加上const了。
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
bool operator==(const Date& d)const//因为不用改变值所以可以用const
{
return (_year == d._year && _moth == d._moth && _day == d._day);
}
private:
int _year;
int _month;
int _day;
};
bool operator==(const Date& d)const 第一个const是修饰传过来的参数,第二个const是修饰this指针的,不让成员变量修改,这样可以减少错误的发生。(bool是返回类型)
3.友元
友元分为:友元函数和友元类
在类中 一般成员变量不想让别人看见、修改。所以设置成非公有的,要通过类内的成员函数(一般为公有)访问。其他类和其他函数是不能直接访问类内成员变量,但友元可以让他们实现,非常的方便,但方便的同时,关联性更强了以后想要修改其中一个就变得很麻烦。所以不宜多用。
3.1友元函数
友元函数可以直接访问类的私有成员,它是定义在类外部的普通函数,不属于任何类,但需要在类的内部声明,声明时需要加friend关键字。(和普通函数一样就是要在类内加一个声明,声明的前面加一个friend)
问题:现在我们尝试去重载operator<<,然后发现我们会发现很奇怪,一般的是这样重载。
ostream& operator<<(ostream& out)//cout是ostream这个类型
{
out << _year << '-' << _moth << '-' << _day << endl;
return out;
}
但是为什么cout<<d;会报错而d<<cout;却可以用呢?是因为<<的左边是第一个参数,<<右边是第二个参数,而在上面代码this指针(隐藏起来了)在第一个位置,out在第二个位置,这个位置位置反了。所以不能让this指针放第一个参数。那就不能是类的成员函数,但如果放在类外面定义,那就不能访问类内的成员变量了,这时就要利用我们的友元函数了。如图下代码。
class Date
{//如下就是友元函数的声明
friend ostream& operator<<(ostream& _cout, const Date& d);
friend istream& operator>>(istream& _cin, Date& d);
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& _cout, const Date& d)
{
_cout<<d._year<<"-"<<d._month<<"-"<<d._day;
return _cout;
}
istream& operator>>(istream& _cin, Date& d)
{
_cin>>d._year;
_cin>>d._month;
_cin>>d._day;
return _cin;
}
int main()
{
Date d;
cin>>d;
cout<<d<<endl;
return 0;
}
注意的小点
1.友元函数可访问类的私有和保护成员,但不是类的成员函数
2.友元函数不能用const修饰(因为没有this指针,函数用const是修饰this的)
3.友元函数可以在类定义的任何地方声明,不受类访问限定符限制
4.一个函数可以是多个类的友元函数
5.友元函数的调用与普通函数的调用和原理相同
3.2友元类
友元类的所有成员函数都可以是另一个类的友元函数,都可以访问另一个类中的非公有成员。
1.友元关系是单向的,不具有交换性。 比如上述Time类和Date类,在Time类中声明Date类为其友元类,那么可以在Date类中直接访问Time类的私有成员变量,但想在Time类中访问Date类中私有的成员变量则不行。(如下代码)
2.友元关系不能传递 如果B是A的友元,C是B的友元,则不能说明C时A的友元。
class Date; // 前置声明
class Time
{
friend class Date; // 声明日期类为时间类的友元类,则在日期类中就直接访问Time类中的私有成
员变量
public:
Time(int hour, int minute, int second)
: _hour(hour)//用了初始化列表
, _minute(minute)
, _second(second)
{}
private:
int _hour;
int _minute;
int _second;
};
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1)
: _year(year)//用了初始化列表
, _month(month)
, _day(day)
{}
void SetTimeOfDate(int hour, int minute, int second)
{
// 直接访问时间类私有的成员变量
_t._hour = hour;
_t._minute = minute;
_t.second = second;
}
private:
int _year;
int _month;
int _day;
Time _t;
};