友元函数
概念(了解)
友元函数是一种特殊类型的函数,它能够访问类的私有成员和保护成员。这意味着,即使这些成员被声明为私有或保护的,友元函数也可以在类外部直接访问它们。
友元函数的作用主要有以下几个方面:
-
提高程序的效率:如果某个函数需要频繁地访问类的私有成员或保护成员,将其声明为友元函数可以避免通过类的公共接口来访问这些成员,从而提高程序的效率。
-
增加代码的灵活性:友元函数可以访问类的私有成员和保护成员,这使得在某些情况下可以更方便地实现某些功能。例如,当我们需要对类进行序列化和反序列化时,可以定义一个友元函数来访问类的私有成员,从而更容易地实现序列化和反序列化的功能。
-
避免破坏封装性:如果不使用友元函数,那么在某些情况下,为了访问类的私有成员或保护成员,可能需要将这些成员声明为公共的,这将破坏类的封装性。而友元函数可以在不破坏类的封装性的情况下访问类的私有成员或保护成员,从而更好地保护类的封装性。
友元函数实现
关键词:friend
注意事项:
- 函数参数是对象引用
- 不是类内函数,不受类内权限修饰符限制。友元函数的声明可以写在类的任意位置
#include <iostream>
using namespace std;
class Person{
private:
string name;
int age;
string passwd;
public:
Person(string name,int age,string passwd):
name(name),age(age),passwd(passwd){}
friend void test(Person &other);//添加friend关键字就可访问
};
//类外函数
void test(Person &other){
cout<<other.name<<endl;
cout<<other.age<<endl;
cout<<other.passwd<<endl;
}
int main()
{
Person s1("nana",19,"145555");
test(s1);
return 0;
}
友元类
当A是B的友元类的时候,A就可以访问B的所有成员
#include <iostream>
using namespace std;
class B{
friend class A;//A是B的友元类
private:
string name;
int age;
string passwd;
public:
B(string name,int age,string passwd):
name(name),age(age),passwd(passwd){}
};
class A{
public:
//A可以访问A的私有成员变量
void test (const B &other) {
cout<<other.name<<endl;
cout<<other.age<<endl;
cout<<other.passwd<<endl;
}
};
int main()
{
B s1("B",100,"114587");
A s2;
s2.test(s1);
return 0;
}
友元类的注意事项:(选择题)
- 友元关系是单向的(a是b的朋友,b不是a的朋友)
- 友元关系不可传递 (a是b的朋友,b和c是朋友,a和c不是朋友)
- 友元关系不可继承
运算符重载
概念
运算符重载是指对C++中的某个运算符进行重新定义,使其可以对用户自定义的数据类型进行操作。通过运算符重载,我们可以为自己定义的类创建一些更加自然的行为,使其可以像内置数据类型一样进行运算。
特点:
- 运算重载只能对已有的运算符赋予多重含义,不能自己定义类型
- 运算符重载的本质是函数重载,它也是C++多态的一种体现
- 运算符重载增强了C++的可扩充性,使得C++代码更加直观、易读
关键词:operator
组成:它们的名字由关键字operator和其后要重载的运算符共同组成。
加法运算符重载
可以使用两种方式完成重载
全局函数法(友元函数法)
#include <iostream>
using namespace std;
class Test{
friend Test operator+ (Test &s1,Test &s2);
private:
int val1;
int val2;
public:
Test(int val1,int val2):val1(val1),val2(val2){}
void show(){
cout<<val1<<endl;
cout<<val2<<endl;
}
};
//全局函数,和类本身无关,想要访问得是类的友元函数
Test operator+ (Test &s1,Test &s2)
{
Test temp(s1.val1+s2.val1,s2.val2+s2.val2);
return temp;
}
int main()
{
Test s1(10,20);
Test s2(10,30);
Test s3=s1+s2;//Test s3=s1+s2;//普通加运算符不支持这种算法
s3.show();
return 0;
}
成员函数法
#include <iostream>
using namespace std;
class Test{
private:
int val1;
int val2;
public:
Test(int val1,int val2):val1(val1),val2(val2){}
void show(){
cout<<val1<<endl;
cout<<val2<<endl;
}
Test operator+(const Test& other);
};
Test Test::operator+(const Test& other){
Test temp(this->val1+other.val1,this->val2+other.val2);
return temp;
}
int main()
{
Test s1(10,20);
Test s2(10,30);
Test s3=s1+s2;//Test s3=s1+s2;//普通加运算符不支持这种算法
s3.show();
return 0;
}
注意事项:
从上面成员函数实现可以看出,使用成员函数法实现前提是第一个参数是对象类型,才能调用this指针指针进行操作。但是如果运算符前面的第一个参数不是对象类型,这时必须选用友元函数重载。
++运算符
++运算符分为前置++和后置++
友元函数法
#include <iostream>
using namespace std;
class Test{
private:
int val;
public:
Test(int val):val(val){}
void show(){
cout<<val<<endl;
}
friend Test operator++(Test&other);
friend Test operator++(Test&other,int);
};
//前置++
Test operator++(Test&other){
other.val++;
Test temp(other);
return temp;
}
//后置++
Test operator++(Test&other,int){//后置++使用int进行区分
Test temp(other);
other.val++;
return temp;
}
int main()
{
Test s1(10);
++s1;
s1.show();
s1++;
s1.show();
return 0;
}
成员函数法
#include <iostream>
using namespace std;
class Test{
private:
int val;
public:
Test(int val):val(val){}
void show(){
cout<<val<<endl;
}
//前置++
Test operator++(){
this->val++;
return *this;
}
//后置++
Test operator++(int){
Test temp(this->val);
this->val++;
return temp;
}
};
int main()
{
Test s1(10);
++s1;
s1.show();
s1++;
s1.show();
return 0;
}
注意事项:
上面的实现的方式不是唯一解法,还有很多别的解法
进行++重载的时候,用到了哑元函数的知识,使用int进行区分
赋值运算符重载(存在默认的赋值运算符函数)
形式:T&operator=(const T&other)简答题考过
作用:用一个对象的值改另一个对象的值
注:当不显示的写出赋值运算符重载时会有默认的赋值运算符重载。
如果类中有指针类型的时候,默认赋值运算符函数只会简单地址值的复制。破坏对象之间的独立性(类似深浅拷贝),这时就需要显示写出赋值运算符函数
#include <iostream>
using namespace std;
class Test{
private:
int val;
public:
Test(int val):val(val){}
void show(){
cout<<val<<endl;
}
Test &operator=(const Test&other){
cout<<"我是赋值构造函数"<<endl;
this->val=other.val;
return *this;
}
};
int main()
{
Test s1(10);
Test s2(20);
s1=s2;
s1.show();
return 0;
}
类型转换运算符
特点:只支持成员函数重载,不需要写返回值类型(唯一区别)
作用:把对象类型转成任意类型
#include <iostream>
using namespace std;
class Test{
private:
int val;
string name;
public:
Test(int val,string name):val(val),name(name){}
operator string(){
return name;
}
operator int(){
return val;
}
};
int main()
{
Test s1(100,"string");
string str=s1;
cout<<str<<endl;
int a=s1;
cout<<s1<<endl;
return 0;
}
运算符重载注意事项
- 运算符重载限制在C++已有的运算符范围内,不允许创建新的运算符。
- 运算符重载也是函数重载,运算符也是函数。
- 重载之后的运算符不能改变优先级和结合性。
- 重载之后的运算符不能改变操作数和语法结构。
- 运算符重载不能改变该运算符用于基本数据类型的含义,但是可以把基本数据类型与自定义类型一起运算,或者都使用自定义类型。
- 运算符重载是针对新类型数据的实际需要对原有运算符的功能进行扩充,因此重载之后的功能应该与原有的功能类似,避免没有目的地使用运算符重载。
string类
string是c++提供给我们的,但是string类有很多方法得了解一下
#include <iostream>
using namespace std;
int main()
{
string s="world";
//字符串长度
cout<<s.size()<<endl;//5
cout<<s.length()<<endl;//5
//判空
cout<<s.empty()<<endl;//0,非空
//拷贝构造
string s3(s);
cout<<s3<<endl;//world
//增,字符串支持相加
string s1="hello ";
cout<<s1+s<<endl;//hello world
s.append(" world2");
cout<<s<<endl;//world world2
//删
s.erase(5,7);//world
//第一个参数为删除起始下标
//第二个参数为删除个数
cout<<s<<endl;
//改
//可以直接通过下标进行修改
s[0]='f';
cout<<s<<endl;//fword
s.replace(1,1,"good");
cout<<s<<endl;//fgoodrld
//字符串默认比较的是编码
string st1="hello";
string st2="world";
cout<<(st1==st2)<<endl;// 0 比较编码
cout<<(st1<st2)<<endl; // 1
return 0;
}