C++(运算符重载)
一.友元
C++中使用关键字friend可以在类外访问所有的成员,包括私有成员(之前提到过封装的核心思想是隐藏内部实现细节,通过公共接口控制访问),所以友元可以突破封装的限制访问数据,盲目使用会导致程序稳定性降低,所以使用友元必须慎重。
友元分类:
友元函数
友元类
友元成员函数
1.友元函数
全局函数访问私有成员,可以配合运算符重载使用,友元函数是一种在类内“声明”,类外定义的非成员函数,但是没访问类内所有的成员。友元函数可以做到修改私有成员变量
注意:
(1)友元函数没有this指针。
(2)友元函数可以在类的私有部分,也可以在类的公有部分。
(3)一个函数可以是多个类的友元函数,只需要在不同的类汇中声明。
#include <iostream>
#include <string>using namespace std;class Techer{
private:int age = 45;string name = "zhangsna";
public:Techer(int age,string name):age(age),name(name){}//Techer():age(45),name("lisi"){}int get_age()const{return age;}string get_name()const{return name;}void print()const{cout << get_age() << endl;cout << get_name() << endl;}int print_age(){return 18;}friend void print_real_age(Techer &t); //类内声明友元函数
};void print_real_age(Techer &t){ //类外定义友元函数cout << "真实年龄:" << t.age << endl;cout << "虚假年龄:" << t.print_age() << endl;t.age = 30;cout << "修改年龄:" << t.age << endl;
}int main (){Techer t1(34,"zhangsan");cout << t1.print_age() << endl;print_real_age(t1);return 0;
}
2.友元类
当B是A的友元类时,类B就可以访问类A的所有成员,
需要注意的是:
(1)友元类不具备交换性:A声明B为友元 → B可访问A的私有成员,但A不能访问B的私有成员。
(2)友元类不具备传递性:A是B的友元,B是C的友元 → A不是C的友元。
(3)友元类不具备继承性:基类友元不会继承给派生类。
#include <iostream>
#include <string>using namespace std;class Techer{
private:int age = 45;string name = "zhangsna";
public:Techer(int age,string name):age(age),name(name){}//Techer():age(45),name("lisi"){}int get_age()const{return age;}string get_name()const{return name;}void print()const{cout << get_age() << endl;cout << get_name() << endl;}int print_age(){return 18;}friend class student;
};class Student{
public:void get_name(Techer &t){cout << t.get_name() << endl;}
};int main (){Techer t1(34,"zhangsan");Student s1;s1.get_name(t1);return 0;
}
友元成员函数
#include <iostream>
#include <string>using namespace std;class Techer; //声明类class Student{
public:void print_name(Techer &t);
};class Techer{
private:int age = 45;string name = "zhangsna";
public:Techer(int age,string name):age(age),name(name){}//Techer():age(45),name("lisi"){}int get_age()const{return age;}string get_name()const{return name;}void print()const{cout << get_age() << endl;cout << get_name() << endl;}int print_age(){return 18;}friend void Student::print_name(Techer &t); //1.友元声明
};//4.实现友元函数
void Student::print_name(Techer &t){cout << t.get_name() <<endl;
}int main (){Techer t1(34,"zhangsan");Student s1;s1.print_name(t1);return 0;
}
二.函数运算符重载
C++中函数是可以重载的,运算符也是一种特殊的函数,也可以重载,这样一来,本来只能基本数据类型能做的加减乘除也可以让对象直接加减乘除。
函数的三要素是:函数名,参数和返回值。
下面就是C++中运算符+含义,所以我们对+函数进行重载,就可以实现对象之间相加。
可以被重载的运算符:(了解)
算术运算符: +、-、*、人、%、++、--
位操作运算符:&、|、~、^(位异或)、<<(左移)、>>(右移)
逻辑运算符:!、&&、
比较运算符:<、>、>=、《=、==、!=
赋值运算符:=、+=、-=、*=、/=、%=、&=、 I=、^=、《<=、>>=
其他运算符:范、0、->、、new、delete、new[、delete[
不被重载的运算符:
成员运算符""、指针运算符“*”、三目运算符"?:“、sizeof、作用域""
1.使用友元函数运算符重载
函数名:+
参数:const Integer &i1和const Integer &i2
返回值:Inteder
friend Integer operator +(const Integer &i1,const Integer &i2);
#include <iostream>
#include <string>using namespace std;class Integer{
private:int value;
public:Integer(int value):value(value){}int get_value()const{return value;}void set_value(int a){value = a;}friend Integer operator +(const Integer &i1,const Integer &i2);friend Integer operator ++(Integer &i);friend Integer operator ++(Integer &i,int);
};Integer operator +(const Integer &i1,const Integer &i2){return i1.value + i2.value; //返回两个int类型,这个时候编译器会自动帮你创建一个Integer类型的副本,类似于:return Integer(i1.value + i2.value);
}
Integer operator ++(Integer &i){return ++i.value;
}
Integer operator ++(Integer &i,int){return i.value++;
}int main (){Integer i1(3);Integer i2(4);Integer i3 = i1 + i2;cout << i3.get_value() << endl;++i3;cout << (i3++).get_value() << endl;cout << i3.get_value() << endl;return 0;
}
2.使用成员函数运算符重载
简单三步实现:(投机取巧的方式)
(1).把友元函数实现的代码的friend去掉
(2).再把所有传入的第一个参数去掉,函数内使用this指针替换(不加也可以,编译器自动识别)
(3).在函数名前加上作用域限定符(Integer::)
正常直接写,就是写成员函数的步骤。
#include <iostream>
#include <string>using namespace std;class Integer{
private:int value;
public:Integer(int value):value(value){}int get_value()const{return value;}void set_value(int a){value = a;}Integer operator +(const Integer &i);Integer operator ++();Integer operator ++(int);
};Integer Integer::operator +(const Integer &i){return value + i.value;
}
Integer Integer::operator ++(){return ++value;
}
Integer Integer::operator ++(int){return value++;
}int main (){Integer i1(3);Integer i2(4);Integer i3 = i1 + i2;cout << i3.get_value() << endl;++i3;cout << (i3++).get_value() << endl;cout << i3.get_value() << endl;return 0;
}
3.特殊运算符重载
3.1复制运算符重载
如果程序员不手写赋值运算符重载函数,则编译器会自动为每个类增加一个赋值运算符重载函数。
在 C++ 中,赋值运算符(operator=
)必须被重载为类的成员函数,而不能作为友元函数或全局函数。这一设计决策源于 C++ 语言的核心特性:
(1)、根本原因:语言规范要求
C++ 标准(ISO/IEC 14882)明确规定:赋值运算符(
=
)必须被声明为类的非静态成员函数。这是语言规范层面的硬性规定,违反此规则将导致编译错误。(2)、技术原因:this 指针机制
1. 成员函数的隐式 this 指针
成员函数自动获得隐式
this
指针,指向调用对象赋值操作需要修改左操作数的状态,
this
提供直接访问2. 友元函数缺少 this 指针
友元函数没有隐式
this
指针必须显式声明两个参数(左操作数和右操作数)
违反赋值运算符的二元操作符语法形式
#include <iostream>
#include <string>using namespace std;class String{
private:string str;
public:String(string str):str(str){}string get_str(){return str;}void set_str1(string str){this->str = str;}String& operator =(const String& a){ //系统默认自带的this->str = a.str;return *this;}
};int main (){String s1("hello ");String s2("world");s1 = s2;cout << s1.get_str() << endl;return 0;
}
3.2类型转换运算符重载
如果我们想把string类型转换为成员变量,如果正好你的成员变量只有一个string类型的成员变量,这个时候编译器会自动帮你赋值优化,String s1 = name;就不会报错,但是你要是想把s1对象赋值给string类型的name,这个时候编译器就无法识别,需要使用成员函数进行重载。
可以把注释的部分去掉,错误就没了,注视部分就是类型转换重载。
#include <iostream>
#include <string>using namespace std;class String{
private:string str;
public:String(string str):str(str){}string get_str(){return str;}void set_str1(string str){this->str = str;}/*operator string(){return str;}*/
};int main (){String s1("hello ");String s2("world");s1 = s2;string name = s1;cout << name << endl;cout << s1.get_str() << endl;return 0;
}
4.注意:
1.重载的运算符只能对C++语言中已有的运算符进行操作,不能创建新的运算符;
2.运算符重载也是函数重载;
3.运算符不能改变运算符的优先级(*的优先级比+大,不能改变)和结合性(a*b就是a*b不能ab*运算),也不能改变操作数和语法结构;
4.运算符重载函数不支持默认参数,即运算符的操作数数量固定(如二元运算符需要两个操作数)
5.运算符重载函数的操作数中一定包含自定义类型(运算符重载函数的操作数中必须至少有一个是用户定义类型)
三.string字符串常用手册
C++函数手册:
通过网盘分享的文件:C++函数手册 (LibraryFunctions).chm
链接: https://pan.baidu.com/s/17gE3aR6VQhQQ10tBZNFfHg 提取码: 3589
#include <iostream>
#include <string.h>using namespace std;int main()
{string s1 = "ABC";// 隐式调用了下面的,参数为const char*string s2("ABC");string s3 = s2; // 拷贝构造函数cout << s3 << endl; // ABC// 参数1:const char*// 参数2:保留前几个字符string s4("ABCDE",2);cout << s4 << endl; // ABs1 = "ABCDE";// 参数1:string// 参数2:不保留前几个字符string s5(s1,2);cout << s5 << endl; // CDE// 参数1:字符数量// 参数2:字符内容string s6(3,'Z');cout << s6 << endl; // ZZZswap(s5,s6);cout << s5 << " " << s6 << endl; // ZZZ CDE// 向后追加字符串cout << s1.append("...").append(s5) << endl;// 向后追加单字符s6.push_back('O');cout << s6 << endl; // CDEO// 参数1:插入的位置// 参数2:插入的内容s6.insert(1,"222");cout << s6 << endl;// 参数1:删除的起始位置// 参数2:删除的字符数s6.erase(1,3);cout << s6 << endl;// 参数1:替换的起始位置// 参数2:替换的字符数// 参数3:替换的字符s6.replace(0,3,"xxxxxxx");cout << s6 << endl;s6.clear(); // 清空// 判断是否为空cout << s6.empty() << endl; // 1char c[20];s6 = "ABCDEFG";// 参数1:拷贝的目标// 参数2:拷贝的字符数// 参数3:拷贝的起始点s6.copy(c,2,3);cout << c << endl;char c2[20];s6 = "ABCDEFG";// c_str返回的char*指向一个内部数组,相对不可控cout << s6.c_str() << endl;// 因此需要拷贝出来strcpy(c2,s6.c_str());cout << c2 << endl;return 0;
}
练完后会不会有个疑问,为什么string有这么多操作,之前说过他不是基本数据类型,因为它就是C++中定义好的类。