嵌入式学习-C嘎嘎-Day03
[1. 友元 friend](#1. 友元 friend)
[1.1 概念](#1.1 概念)
[1.2 友元函数](#1.2 友元函数)
[1.3 友元类](#1.3 友元类)
[1.4 友元成员函数](#1.4 友元成员函数)
[2. 运算符重载](#2. 运算符重载)
[2.1 概念](#2.1 概念)
[2.2 友元函数运算符重载](#2.2 友元函数运算符重载)
[2.3 成员函数运算符重载](#2.3 成员函数运算符重载)
[2.4 特殊运算符重载](#2.4 特殊运算符重载)
[2.4.1 赋值运算符重载](#2.4.1 赋值运算符重载)
[2.4.2 类型转换运算符重载](#2.4.2 类型转换运算符重载)
[2.5 注意事项](#2.5 注意事项)
[3. 字符串类型 string](#3. 字符串类型 string)
1. 友元 friend
1.1 概念
定义:
类实现了数据的隐藏与封装,类的成员变量一般定义为私有成员,仅能通过类的成员函数才能读写。如果成员变量定义为公共的,则又破坏了封装性。但是某些情况下,需要频繁读写类的成员变量,特别是在对某些成员函数多次调用时,由于参数传递、类型检查和安全性检查等都需要时间开销,而影响程序的运行效率。
友元是一种定义在类外部的普通函数,但他需要在类体内进行说明,为了和该类的成员函数加以区别,在说明时前面加以关键字friend。友元不是成员函数,但是他能够访问类中的私有成员。
作用:
在于提高程序的运行效率,但是,他破坏了类的封装性和隐藏性,使得非成员函数能够访问类的私有成员。导致程序维护性变差,因此使用友元要慎用。
友元主要的应用场景是运算符重载。
用法:
- 友元函数
- 友元类
- 友元成员函数
1.2 友元函数
友元函数是一种在类内说明,但本身属于类外的函数,可以访问类内所有成员。
#include <iostream>`
`using namespace std;`
`class Girl`
`{`
`private:`
` int age;`
`public:`
` Girl(int age):age(age){}`
` int get_age() const`
` {`
` return 18;`
` }`
` // 友元关系说明`
` friend void get_real_age(Girl& g);`
`};`
`void get_real_age(Girl& g)`
`{`
` // 获取私有成员的数值并修改`
` cout << "真实年龄:" << g.age << endl;`
` g.age = 18;`
` cout << "修改后的真实年龄:" << g.age << endl;`
`}`
`int main()`
`{`
` Girl g(40);`
` cout << g.get_age() << endl; // 18`
` get_real_age(g);`
`}
需要注意的是:
- 友元函数没有this指针
- 友元函数的类内说明可以在任何部分,不受权限的影响
- 一个函数可以是多个类的友元函数,只需要分别在个各类中说明
1.3 友元类
当一个类B成为了另一个类A的友元类时,类A的所有成员就可以被类B访问了。
#include <iostream>`
`using namespace std;`
`class A`
`{`
`private:`
` int value = 1;`
` // 友元关系说明`
` friend class B;`
`};`
`class B`
`{`
`public:`
` void access(A& a)`
` {`
` cout << this << endl;`
`// cout << this->value << endl; 错误`
` // 读取和修改a的private成员`
` cout << a.value++ << endl;`
` cout << a.value << endl;`
` }`
`};`
`int main()`
`{`
` A a;`
` B b;`
` b.access(a);`
`}
需要注意的是:
- 友元关系不能被继承
- 友元关系不具有交换性
- 友元关系不具有传递性
1.4 友元成员函数
#include <iostream>`
`using namespace std;`
`// 3. 上一步用到了A类,补充A声明`
`class A;`
`// 2. 上一步中用到了B类,补充B类`
`class B`
`{`
`public:`
` void access(A& a);`
`};`
`class A`
`{`
`private:`
` int value = 1;`
` // 1. 说明友元关系`
` friend void B::access(A& a);`
`};`
`// 4. 补充友元成员函数的内容`
`void B::access(A &a)`
`{`
` cout << a.value++ << endl;`
` cout << a.value << endl;`
`}`
`int main()`
`{`
` A a;`
` B b;`
` b.access(a);`
`}`
`
2. 运算符重载
2.1 概念
函数可以重载,在C++中运算符也可以重载,运算符默认的操作类型只能是基本数据类型,但是对于很多用户自定义类型,也需要类似的运算操作,此时可以重载运算符,赋予这些运算符新的功能,执行对于自定义类型的运算操作。
可以被重载的运算符:
算术运算符:+、-、*、/、%、++、--
位操作运算符:&、|、~、^(位异或)、<<(左移)、>>(右移)
逻辑运算符:!、&&、||
比较运算符:<、>、>=、<=、==、!=
赋值运算符:=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=
其他运算符:[]、()、->、,、new、delete、new[]、delete[]
不被重载的运算符:
成员运算符"."、指针运算符"*"、三目运算符"? :"、sizeof、作用域"::"
通常有两种运算符重载的方式(部分运算符只支持一种):
- 友元函数运算符重载
- 成员函数运算符重载
可以把运算符重载看做是一种特殊的函数重载,运算符是一种特殊的函数。
2.2 友元函数运算符重载
#include <iostream>`
`using namespace std;`
`/**`
` * @brief The Integer class 整数类`
` */`
`class Integer`
`{`
`private:`
` int value;`
`public:`
` Integer(int v):value(v){}`
` int get_value() const`
` {`
` return value;`
` }`
` 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;`
`}`
`Integer operator ++(Integer& i)`
`{`
` return ++i.value;`
`}`
`Integer operator ++(Integer& i,int)`
`{`
` return i.value++;`
`}`
`int main() {`
` Integer i1(1);`
` Integer i2(2);`
` Integer i3 = i1+i2;`
` cout << i3.get_value() << endl; // 3`
` cout << (++i3).get_value() << endl; // 4`
` cout << (i3++).get_value() << endl; // 4`
` cout << i3.get_value() << endl; // 5`
` return 0;`
`}`
`
2.3 成员函数运算符重载
成员函数运算符重载与友元函数运算符重载的最大区别是,成员函数运算符重载的输入参数比友元函数运算符重载少一个,因为友元函数的第一个参数在成员函数中使用this指针表示。
#include <iostream>`
`using namespace std;`
`/**`
` * @brief The Integer class 整数类`
` */`
`class Integer`
`{`
`private:`
` int value;`
`public:`
` Integer(int v):value(v){}`
` int get_value() const`
` {`
` return value;`
` }`
` Integer operator +(const Integer& i);`
` Integer operator ++(); // 前置`
` Integer operator ++(int); // 后置`
`};`
`Integer Integer::operator +(const Integer& i)`
`{`
` // this指针可以不写,因为只要不重名编译器自动添加`
` return this->value + i.value;`
`}`
`Integer Integer::operator ++()`
`{`
` return ++this->value;`
`}`
`Integer Integer::operator ++(int)`
`{`
` return this->value++;`
`}`
`int main() {`
` Integer i1(1);`
` Integer i2(2);`
` Integer i3 = i1+i2;`
` cout << i3.get_value() << endl; // 3`
` cout << (++i3).get_value() << endl; // 4`
` cout << (i3++).get_value() << endl; // 4`
` cout << i3.get_value() << endl; // 5`
` return 0;`
`}`
`
2.4 特殊运算符重载
2.4.1 赋值运算符重载
如果程序员不手动在类中编写赋值运算符重载函数,编译器会为这个类自动添加一个默认的赋值运算符重载函数,以便于实现同类型对象的赋值操作。
赋值运算符重载函数只能通过成员函数实现,因为需要this指针。
需要注意的是,如果出现了浅拷贝,也要修改赋值运算符重载函数的逻辑。
#include <iostream>`
`using` `namespace std;`
`/**`
` * @brief The Integer class 整数类`
` */`
`class` `Integer`
`{`
`private:`
`int value;`
`public:`
`Integer(int v):value(v){}`
`int` `get_value()` `const`
`{`
`return value;`
`}`
`// 默认的赋值运算符重载函数`
` Integer&` `operator` `=(const Integer& i)`
`{`
`this->value = i.value;`
`return` `*this;`
`}`
`};`
`int` `main()` `{`
` Integer i1(1);`
` Integer i2(2);`
` i1 = i2;` `// 赋值运算`
` cout << i1.get_value()` `<< endl;` `// 2`
`return` `0;`
`}`
`
【思考】截止到目前,如果一个成员手写了一个空类,编译器会自动添加哪些内容:
- 构造函数
- 析构函数
- 拷贝构造函数
- 赋值运算符重载函数
2.4.2 类型转换运算符重载
类型转换运算符重载函数也必须使用成员函数重载,格式比较特殊
#include <iostream>`
`using` `namespace std;`
`/**`
` * @brief The Integer class 整数类`
` */`
`class` `Integer`
`{`
`private:`
`int value;`
`public:`
`Integer(int v):value(v){}`
`int` `get_value()` `const`
`{`
`return value;`
`}`
`// 类型转换函数`
`operator` `int()`
`{`
`return value;`
`}`
`};`
`int` `main()` `{`
`// int → Integer`
` Integer i1 =` `1;` `// 编译器自动调用构造函数`
` cout << i1.get_value()` `<< endl;` `// 1`
`// Integer → int`
`int i2 = i1;`
` cout << i2 << endl;` `// 1`
`return` `0;`
`}`
`
2.5 注意事项
- 运算符重载只能限制在C++已有的范围内,不能创建的新的运算符。
- 运算符重载不能改变运算符的优先级和结合性。
- 运算符重载不能改变运算符的操作数和语法结构。
- 运算符重载的操作数一定包含自定义类型。
- 运算符重载的功能应该与原有功能相似,避免滥用。
- 运算符重载不能设置参数默认值。
- 通常单目运算符优先考虑成员函数,双目运算符优先考虑友元函数。
3. 字符串类型 string
之前对string的学习比较基础,实际上string有诸多字符串处理函数。
#include <iostream>`
`#include <string.h>`
`using namespace std;`
`int main() {`
` string s; // 创建一个空字符串`
` cout << s.empty() << endl; // 判断内容是否为空`
` // 构造函数,参数const char*`
` string s1 = "Thursday";`
` string s2("Thursday");`
` cout << (s1 == s2) << endl;`
` // 拷贝构造函数`
` string s3 = s1;`
` string s4(s3);`
` cout << s3 << " " << s4 << endl;`
` // 参数1:const char* 原字符串`
` // 参数2:保留前几个字符`
` string s5("ABCDEFG",2);`
` cout << s5 << endl; // AB`
` s = "ABCDEFG";`
` // 参数1:string 原字符串`
` // 参数2:不保留前几个字符`
` string s6(s,2);`
` cout << s6 << endl; // CDEFG`
` // 参数1:数量`
` // 参数2:字符元素`
` string s7(5,'A');`
` cout << s7 << endl; // AAAAA`
` swap(s6,s7); // 交换`
` cout << s6 << endl;`
` cout << s7 << endl;`
` // 连接`
` cout << s6 + s7 << endl;`
` // 尾插单字符`
` s6.push_back('B');`
` cout << s6 << endl;`
` // 尾插string,支持链式调用`
` s6.append("CC").append("DDD");`
` cout << s6 << endl;`
` // 在第二个位置插入内容"111"`
` s6.insert(1,"111");`
` cout << s6 << endl;`
` // 参数1:替换的起始位置`
` // 参数2:替换的字符数`
` // 参数3:替换的新内容`
` s6.replace(0,3,"*****");`
` cout << s6 << endl;`
` s6.pop_back(); // 删除最后一个字符`
` cout << s6 << endl;`
` s6 = "1234567890";`
` // 参数1:删除的起始位置`
` // 参数2:删除的字符数`
` s6.erase(3,4);`
` cout << s6 << endl;`
` s6.clear(); // 清空`
` cout << s6.size() << endl; // 0`
` s6 = "ABCDEFGHIJK";`
` char c[20];`
` // 参数1:拷贝的目标`
` // 参数2:拷贝的字符数量`
` // 参数3:拷贝的起始位置`
` s6.copy(c,5,2);`
` cout << c << endl;`
` // C++ string→C string`
` strcpy(c,s6.c_str());`
` cout << c << endl;`
` return 0;`
`}`
`