目录
[【1】友元 friend](#【1】友元 friend)
[【3】String 字符串类](#【3】String 字符串类)
【1】友元 friend
1》概念
定义:
类实现了数据的隐藏和封装,类的数据成员一般定义为私有成员,仅能通过类的成员函数才能访问到。如果数据成员定义为公共的,则又破坏了封装性。但是某些情况下,需要频繁访问类的成员,特别是在对某些成员函数多次调用时,由于参数传递、类型检查和安全性检查等都需要时间开销,会影响到程序的运行效率。
友元是一种定义在类外部 的普通函数,蛋挞需要在类内进行声明,为了和该类的成员函数加以区别,要在声明前加关键字friend 。友元不是成员函数,但是它能够访问类内的所有成员。
作用:
提高程序的运行效率,但是破坏了类的封装性和隐藏性,使得非成员函数能够访问类的私有成员。导致程序维护性变差,所以使用友元要慎重。
友元更为常用的应用是**运算符重载,**这种应用可以提高软件系统的灵活性。
分类:
1.友元函数
2.友元类
3.友元成员函数
2》友元函数
友元函数是一种"声明"在类内,实际在类外的普通函数。
cpp
#include <iostream>
using namespace std;
class Girl
{
private:
int age;
public:
Girl(int age):age(age){}
int get_age() const
{
cout << &age << endl;
return 18;
}
// 1. "声明"友元函数
friend void access_true_age(Girl&);
};
// 2. 定义友元函数
void access_true_age(Girl& g)
{
// 突破权限,直接操作Girl类的私有成员;
cout << &g.age << endl;
cout << "真实年龄:" << g.age << endl;
// 修改
g.age = 18;
cout << "修改后年龄:" << g.age << endl;
}
int main()
{
Girl g(45);
cout << g.get_age() << endl;
// 通过友元函数访问Girl的年龄
access_true_age(g);
return 0;
}
需要注意的是:
1.由于友元函数不属于成员函数,因此友元函数没有this 指针,访问类的成员的时候只能通过对象
2.友元函数在类中的"声明"可以写在类的任何部分,不受权限修饰符的影响。
3.理论上,一个友元函数可以是多个类的友元函数,只需要在各个类中分别"声明"。
3》友元类
当一个类B成为了另一个类A的友元类的时候,类B可以访问类A的所有成员。
需要注意的是:
1.友元关系是单向的,不具有交换性。
如果类B是类A的友元类,类A不一定是类B的友元类
2.友元关系不具有传递性
如果类C是类B的友元类,类B是类A的友元类,类C不一定是类A 的友元类。
3.友元关系不能被继承
cpp
#include <iostream>
using namespace std;
class A
{
private:
string str = "A私有";
// "声明"友元类
friend class B;
};
class B
{
public:
void func(A& a)
{
// cout << this->str << endl; 错误:this是B对象不是A对象
cout << a.str << endl;//可以直接操作类A的私有成员
a.str = "我改了";
cout << a.str << endl;
}
};
int main()
{
A a;
// cout << a.str << endl; 错误,全局不能调用类A的私有成员
B b;
b.func(a);
return 0;
}
4》友元成员函数
在友元类的任何成员函数中都可以访问其他类的成员,但是友元成员函数把友元范围限制在一个成员函数中。
例如,类B的某个成员函数称为了类A 的友元成员函数,这样类B的该成员函数就可以访问两类A的所有成员了。
cpp
#include <iostream>
using namespace std;
// 3. 因为第二步中用到了类A,提前声明类A
class A;
// 2. 编写类B,并真正声明友元成员函数
class B
{
public:
void func(A&);
};
class A
{
private:
string str = "A私有";
// 1. 确定友元的函数格式并"声明"
friend void B::func(A&);
};
// 4. 类外定义友元成员函数
void B::func(A & a)
{
// cout << this->str << endl; 错误:this是B对象不是A对象
cout << a.str << endl;
a.str = "我改了";
cout << a.str << endl;
}
int main()
{
A a;
// cout << a.str << endl; 错误
B b;
b.func(a);
return 0;
}
【2】运算符重载
1》概念
如果把运算符看做是一个函数,则运算符也可以想函数一样重载。
C++中预定义的运算符的操作对象只能是基本数据类型。但实际上对于很多用户的自定义类型,也是需要类似的运算操作,这时可以在C++中重新定义这些运算符,赋予已有运算符新的功能,使它能够用于特定类型,执行特定的操作。
可以被重载的运算符:
算术运算符:+、-、*、/、%、++、--位操作运算符:&、|、~、^(位异或)、<<(左移)、>>(右移)
逻辑运算符:!、&&、||
比较运算符:<、>、>=、<=、==、!=
赋值运算符:=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=
其他运算符:[]、()、->、,、new、delete、new[]、delete[]
不被重载的运算符:
成员运算符"."、指针运算符"*"、三目运算符"? :"、sizeof、作用域"::"
2》友元函数运算符重载
cpp
#include <iostream>
using namespace std;
/**
* @brief The Integer class 整数类
*/
class Integer
{
private:
int value;
public:
Integer(int value):value(value){}
int get_value() const
{
return value;
}
// 友元函数"声明"
friend Integer operator +(const Integer&,const Integer&);
friend Integer operator ++(Integer&); // 前置
friend Integer operator ++(Integer&,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 << (i1++).get_value() << endl; // 1
cout << (++i1).get_value() << endl; // 3
return 0;
}
3》成员函数运算符重载
成员函数运算符重载与友元函数运算符重载的最大区别:友元函数运算符重载的第一个参数,在成员函数中使用this 指针代替,即使用成员函数重载的运算符重载相比友元函数的参数少一个。
cpp
#include <iostream>
using namespace std;
/**
* @brief The Integer class 整数类
*/
class Integer
{
private:
int value;
public:
Integer(int value):value(value){}
int get_value() const
{
return value;
}
// 声明成员函数
Integer operator +(const Integer&); // 双目
Integer operator ++(); // 前置
Integer operator ++(int) // 后置
{
return this->value++;
}
};
Integer Integer::operator +(const Integer& i)
{
return this->value + i.value;
}
Integer Integer::operator ++()
{
return ++this->value;
}
int main()
{
Integer i1(1);
Integer i2(2);
Integer i3 = i1 + i2;
cout << i3.get_value() << endl; // 3
cout << (i1++).get_value() << endl; // 1
cout << (++i1).get_value() << endl; // 3
return 0;
}
4》赋值运算符与类型转换运算符重载
如果程序员不写赋值运算符重载函数,编译器会为类自动添加一个赋值运算符重载函数,且此函数支持链式调用,因此只能使用成员函数运算符重载。
cpp
#include <iostream>
using namespace std;
class Value
{
public:
int value = 0;
// 1. 构造函数
// 2. 拷贝构造
// 3. 析构函数
// 4. 赋值运算符重载
// 编译器自动添加:
Value& operator =(const Value& v)
{
value = v.value;
return *this;
}
};
int main()
{
Value v; // 构造函数
Value v2(v); // 拷贝构造
Value v3 = v2; // 拷贝构造
v.value = 1;
v3 = v; // 赋值运算符
cout << v3.value << endl; // 1
return 0;
}
类型转换运算符与赋值运算符的符号都是 = ,因此类型转换运算符重载函数比较特殊,以便于与赋值运算符重载进行区分,同样类型转换运算符重载函数也只支持成员函数运算符重载。
cpp
#include <iostream>
using namespace std;
class Value
{
private:
int value;
public:
Value(int value):value(value){}
int get_value() const
{
return value;
}
// 类型转换运算符重载函数
operator int()//value转换为int类型
{
return value;
}
};
int main()
{
// int → Value
Value v = 1; // 隐式构造
// Value → int
int i = v; // 类型转换运算符重载函数
cout << i << endl; // 1
return 0;
}
5》注意事项
1.运算符重载限制在C++已有的运算符范围内,不能创建新的运算符。
2.运算符重载不能改变运算符的优先级、结合性、操作数和语法结构。
3.运算符重载不能改变基本类型的计算规则,只能更改包含自定义类型的计算规则。
4.运算符重载实现的功能应该与元运算符相近。
5.运算符重载函数不支持参数默认值。
6.通常单目运算符使用成员函数重载,双目运算符使用友元函数重载。
【3】String 字符串类
cpp
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
string s; // 生成一个空字符串
cout << "判断字符串是否为空:" << s.empty() << endl; // 1
string s1 = "Thursday"; // 隐式调用构造函数,参数const char*
string s2("Thursday"); // 显式调用上面的构造函数
cout << "判断是s1是否等于s2:" << (s1 == s2) << endl; // 1
string s3 = s1; // 隐式调用拷贝构造
string s4(s2); // 显式调用拷贝构造
cout << "判断是s1是否等于s2:" << (s3 == s4) << endl; // 1
s = s4; // 赋值运算符
cout << s << endl; // "Thursday"
// 参数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(6,'A');
cout << s7 << endl; // AAAAAA
// 交换
swap(s6,s7);
cout << s6 << " " << s7 << endl; // AAAAAA CDEFG
s = s6+s7; // 拼接
cout << s << endl; // AAAAAACDEFG
// 向后追加
s.append("123");
cout << s << endl; // AAAAAACDEFG123
// 向后追加单字符
s.push_back('%'); // AAAAAACDEFG123%
// 插入
// 参数1:插入的位置
// 参数2:插入的内容
s.insert(1,"222"); // A222AAAAACDEFG123%
// 参数1:删除的起始位置
// 参数2:删除的字符数
s.erase(4,10);
cout << s << endl; // A222123%
// 参数1:替换的起始位置
// 参数2:替换的字符数
// 参数3:替换的内容
s.replace(0,3,"******");
cout << s << endl; // ******2123%
s.clear(); // 清空
cout << s.length() << endl; // 0
char c[20];
s = "1234567890";
// 参数1:拷贝的目标
// 参数2:拷贝的字符数
// 参数3:拷贝的起始位置
s.copy(c,3,1);
cout << c << endl; // 234
// C → C++ 直接赋值即可
// char* → string
char* c1 = "Tom";
char c2[] = "Jerry";
string sc1 = c1;
string sc2 = c2;
cout << sc1 << "&" << sc2 << endl; // Tom&Jerry
// C++ → C
// string → char[]
s = "abcd";
char ch[10];
strcpy(ch,s.c_str()); // c_str()返回值的const char*不稳定
cout << ch << endl;
return 0;
}
今天的分享就到这里结束啦,如果有哪里写的不好的地方,请指正。
如果觉得不错并且对你有帮助的话点个关注支持一下吧!