友元
类具有数据的隐藏性与封装性,被封装好的数据是通过公开接口访问的,相比于不封装直接访问属性值,执行效率一定是下降的。如果不隐藏与封装,又不符合面向对象的特性
友元函数可以在一个类封装的基础上,突破权限吗,访问到所有的属性值,且没有封装的性能开销(参数传递、类型检查、安全检查等)
友元函数的缺点就是让类的封装变得不可靠,通常只用于运算符重载
友元函数使用主要分为以下几种情况:
友元函数
友元类
友元成员函数
无论哪一种友元的用法,友元都不属于被访问数据的类中的函数
友元函数
class Test
{
private:
int a;
public:
Test(int a):a(a){}
//“声明”友元函数
friend void func(Test& t);
};
//类外定义友元函数
void func(Test& t)
{
cout << ++t.a << endl;
}
友元函数没有this指针,因此需要一个对象参数
友元函数的声明可以在类中的任何位置
理论上,友元函数可以是多个类的友元函数,只需要在类中分别声明
友元类
当一个类b成为另一个类a的友元类时,那么类a中的所有成员都可以被类b访问
class A
{
private:
int value;
public:
A(int value):value(value){}
// 声明友元类
friend class B;
};
class B
{
public:
void test(A& a)
{
// 尽管此函数拥有this指针
// 但是这个this指针指向的不是A对象,而是B对象
cout << ++a.value << endl;
}
};
注意:
友元关系是单向的,不具有交换性
友元关系不具有传递性
友元关系不能被继承
友元成员函数
如果类b的某个成员函数可以访问类a的所有成员,那么这个成员函数就是类a的友元成员函数
友元函数的编写要注意相互依赖关系
// 3. 声明类A
class A;
class B
{
public:
// 2. 声明成员函数
void func(A& a);
};
class A
{
private:
int value;
public:
A(int value):value(value){}
// 1. 确定友元关系:声明友元成员函数
friend void B::func(A& a);
};
// 4. 定义友元成员函数
void B::func(A &a)
{
cout << ++a.value << endl;
}
运算符重载
概念
函数可以重载,运算符也可以重载
C++中运算符默认操作数只能是基本数据类型,但是对于很多程序员自定义的类型,也需要类似的运算操作,这时可以把运算符看作为一个函数,对运算符进行重载,增加新的函数参数类型,使其可以处理自定义类型完成特定的操作
可以被重载的运算符
算数运算符:+、-、、/、%、++、–
位操作运算符:&、|、~、^、<<、>>
逻辑运算符:!、&&、||
比较运算符:<、>、>=、<=、==、!=
赋值运算符:=、+=、-=、=、/=、%=、&=、|=、^=、<<=、>>=
其他运算符:[]、()、->、,、new、delete、new[]、delete[]
不能重载的运算符
成员运算符’.‘、指针运算符’*‘、三目运算符’?‘、sizeof、作用域’::’
运算符重载方式有两种:
友元函数运算符重载
成员函数运算符重载
友元函数运算符重载
无论哪种运算符重载,都要把运算符看作一为个函数
int a = 1;
int b = 2;
int c = a + b;
+:函数名称:operator +
//参数1:a
//参数2:b
//a+b返回值:a+b的结果
class Integer
{
private:
int value;
public:
Integer(int value):value(value){}
int get_value() const
{
return value;
}
// 友元函数运算符重载声明
friend Integer operator +(Integer& i1,Integer& i2);
friend Integer operator ++(Integer& i); // 前置++
friend Integer operator ++(Integer& i,int); // 后置++
};
Integer operator +(Integer& i1,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);
cout << (i1++).get_value() << endl; // 1
cout << (++i1).get_value() << endl; // 3
Integer i3 = i1+i2;
cout << i3.get_value() << endl; // 5
return 0;
}
成员函数运算符重载
相对于友元函数运算符重载,成员函数运算符重载是把友元替换为成员函数,参数的数量
class Integer
{
private:
int value;
public:
Integer(int value):value(value){}
int get_value() const
{
return value;
}
// 成员函数运算符重载声明
Integer operator +(Integer& i);
Integer operator ++(); // 前置++
Integer operator ++(int); // 后置++
};
Integer Integer::operator +(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);
cout << (i1++).get_value() << endl; // 1
cout << (++i1).get_value() << endl; // 3
Integer i3 = i1+i2;
cout << i3.get_value() << endl; // 5
return 0;
}
成员函数运算符重载也可以声明定义不分离
Integer operator ++()
{
return ++this->value;
}
其他情况
当我们在一个类中不写赋值运算符重载的逻辑时,编译器会自动添加赋值运算符重载函数。赋值运算符重载只支持成员函数运算符重载,不支持友元函数运算符重载
class Integer
{
private:
int value;
public:
Integer(int value):value(value){}
int get_value() const
{
return value;
}
// 编译器自动添加
Integer& operator =(const Integer& i)
{
this->value = i.value;
return *this;
}
};
编译器自动添加的赋值运算符重载函数与浅拷贝的逻辑相似,因此当出现指针类型的成员变量时,除了使用深拷贝的构造函数外,还应该重载赋值运算符
另外,类型转换运算符重载也只支持成员函数运算符重载,并且其写法比较特殊
class Integer
{
private:
int value;
public:
Integer(int value):value(value){}
void set_value(int value)
{
this->value = value;
}
int get_value() const
{
return value;
}
//类型转换运算符重载函数
operator int()
{
//返回值就是转换后的数据
return value;
}
};
int main()
{
int i0 = 1;
Integer i1 = i0; // 隐式调用构造函数
cout << i1.get_value() << endl; // 1
i1.set_value(2);
i0 = i1; // 类型转换 Integer → int
cout << i0 << endl; // 2
return 0;
}
std::string类相关函数
#include <iostream>
#include <string.h>
using namespace std;
int main ()
{
string s; // 生成一个空字符串
// 判断字符串是否为空
cout << s.empty() << endl;
// 隐式调用构造函数
string s1 = "abc";
// 上面的写法等效于
string s2("abc");
// 判断内容是否相同
cout << (s1 == s2) << endl;
cout << s1.compare(s2) << endl;
// 拷贝构造函数
string s3(s2);
cout << s3 << endl;
// 参数1:char* 源字符串
// 参数2:保留前几个字符
string s4("ABCDEFG",2);
cout << s4 << endl;
s = "ABCDEFG";
// 参数1:string 源字符串
// 参数2:不保留前几个字符
string s5(s,2);
cout << s5 << endl;
// 参数1:字符数量
// 参数2:字符内容
string s6(5,'A');
cout << s6 << endl;
// 交换
swap(s5,s6);
cout << s5 << " " << s6 << endl;
// 字符串连接符
string s7 = s5 + s6;
cout << s7 << endl;
cout << "aaaaa" + s7 << endl;
// 向后追加字符串,支持链式调用
s7.append("r78w3e").append("fjksd");
cout << s7 << endl;
// 向后追加单字符
s7.push_back('U');
cout << s7 << endl;
// 在制定的位置插入字符串
// 参数1:插入的位置
// 参数2:插入的内容
s7.insert(1,"888");
cout << s7 << endl;
// 删除内容
// 参数1:删除的位置
// 参数2:删除的字符数量
s7.erase(1,4);
cout << s7 << endl;
// 替换字符串
// 参数1:替换的起始位置
// 参数2:替换的字符数量
// 参数3:新替换的内容
s7.replace(1,4,"*****");
cout << s7 << endl;
// 清空
s7.clear();
cout << s7.size() << endl;
char c[10];
s7 = "1234567890";
// 参数1:拷贝到哪个字符数组中
// 参数2:拷贝的字符数量
// 参数3:拷贝的起始位置
s7.copy(c,5,1);
cout << c << endl;
// std::string → char[] 向前兼容
const char* cp = s7.c_str();
cout << cp << endl;
char c2[20];
strcpy(c2,cp);
cout << c2 << endl;
return 0;
}