11.面向对象-封装-友元函数
11.1 知识点
什么是友元
关键字定义:
friend
是C++中用于打破类封装性的关键字。核心作用:允许外部函数或类访问当前类的 私有(private) 或 保护(protected) 成员。
封装性影响:
从面向对象封装原则来看,私有/保护成员本应禁止外部访问。友元机制打破了封装性,可能导致数据安全性降低,因此使用时需谨慎评估场景。
友元函数的概念
友元函数是一个普通函数,虽然需要在类中声明,但是它不属于类的成员
通过友元函数可以访问目标类的私有(private)和保护(protected)成员
友元函数的使用
- 规则:
- 友元函数必须声明在目标类内部
- 需要在函数前面加上 friend 关键字
- 友元函数一般会有一个参数,参数类型为目标类对象
(因为友元函数并不是类的成员函数,无法通过 this 指针访问数据)
#pragma once
class Lesson11
{
public:
int k = 10;
private:
int i = 0;
void test();
protected:
int j = 10;
void test2();
// 友元函数声明 需要在目标类中声明 并且在前面加上friend关键字
// 但是这个函数并不是该类的成员
friend void ChangePrivate(const Lesson11 l);
};
#include "Lesson11.h"
#include <iostream>
using namespace std;
void Lesson11::test()
{
}
void Lesson11::test2()
{
}
// 友元函数 是不属于我们类的 所以没有指定 Lesson11::
// 它是一个全局函数 因为我们是定义在 cpp 中 在任何地方都可以使用它
void ChangePrivate(Lesson11 l)
{
// 公共私有保护的都能访问和修改
l.i = 10;
l.j = 20;
l.k = 30;
cout << l.i << endl;
cout << l.j << endl;
cout << l.k << endl;
l.test();
l.test2();
}
Lesson11 l;
ChangePrivate(l);
友元函数传参的几种方式
- 类型& 参数名
这种方式可读可改成员 - const 类型& 参数名
这种方式比较适用于只读不改,通过引用传递还可以避免不必要的拷贝,并且可以传常量引用和普通引用 - 类型 参数名
不推荐,按值传递,会有拷贝
- 注意:允许传多个参数,根据需求来定,但是至少得有目标对象参数才有意义
#pragma once
class Lesson11
{
public:
int k = 10;
// 属于类的
void ChangePrivateI(int i)
{
this->i = i;
}
private:
int i = 0;
void test();
protected:
int j = 10;
void test2();
// 友元函数声明 需要在目标类中声明 并且在前面加上 friend 关键字
// 但是这个函数并不是该类的成员
friend void ChangePrivate(const Lesson11 l);
friend void ChangePrivate2(const Lesson11& l);
// 属于全局的
friend void ChangePrivateI(Lesson11& l, int i)
{
l.i = i;
}
};
#include "Lesson11.h"
#include <iostream>
using namespace std;
void Lesson11::test()
{
}
void Lesson11::test2()
{
}
// 友元函数 是不属于我们类的 所以没有指定 Lesson11::
// 它是一个全局函数 因为我们是定义在 cpp 中 在任何地方都可以使用它
void ChangePrivate(Lesson11 l)
{
// 公共私有保护的都能访问和修改
l.i = 10;
l.j = 20;
l.k = 30;
cout << l.i << endl;
cout << l.j << endl;
cout << l.k << endl;
l.test();
l.test2();
}
void ChangePrivate2(const Lesson11& l)
{
// 公共私有保护的都能访问和修改
// 加了const后只能访问和调用const修饰的成员和方法了
/*l.i = 10;
l.j = 20;
l.k = 30;*/
cout << l.i << endl;
cout << l.j << endl;
cout << l.k << endl;
/*l.test();
l.test2();*/
}
总结
友元函数本质上是一个特殊的全局函数,该全局函数需要在想要做朋友的类中进行友元声明(前面加上 friend。该全局函数就可以在内部访问到对应类对象的私有或保护的成员。但是一定记住需要将类对象以参数的形式传递进去 因为在内部无法通过 this 去访问成员。
11.2 知识点代码
Lesson11.h
#pragma once
class Lesson11
{
public:
int k = 10;
//属于类的
void ChangePrivateI(int i)
{
this->i = i;
}
private:
int i = 0;
void test();
protected:
int j = 10;
void test2();
//友元函数声明 需要在目标类中声明 并且在前面加上friend关键字
//但是这个函数并不是该类的成员
friend void ChangePrivate(const Lesson11 l);
friend void ChangePrivate2(const Lesson11& l);
//属于全局的
friend void ChangePrivateI(Lesson11& l, int i)
{
l.i = i;
}
};
Lesson11.cpp
#include "Lesson11.h"
#include <iostream>
using namespace std;
void Lesson11::test()
{
}
void Lesson11::test2()
{
}
//友元函数 是不属于我们类的 所以没有指定Lesson11::
//它是一个全局函数 因为我们是定义在cpp中 在任何地方都可以使用它
void ChangePrivate(Lesson11 l)
{
// 公共私有保护的都能访问和修改
l.i = 10;
l.j = 20;
l.k = 30;
cout << l.i << endl;
cout << l.j << endl;
cout << l.k << endl;
l.test();
l.test2();
}
void ChangePrivate2(const Lesson11& l)
{
// 公共私有保护的都能访问和修改
// 加了const后只能访问和调用const修饰的成员和方法了
/*l.i = 10;
l.j = 20;
l.k = 30;*/
cout << l.i << endl;
cout << l.j << endl;
cout << l.k << endl;
/*l.test();
l.test2();*/
}
Lesson11_面向对象_封装_友元函数.cpp
#include <iostream>
#include "Lesson11.h"
int main()
{
#pragma region 知识点一 什么是友元
//友元是C++中的一个关键字:friend(朋友)
//使用友元机制允许
//我们在外部访问某个类的
//私有(private)或保护(protected)的成员
//
//从封装性的角度来看,私有和保护的成员不应该被外部访问到
//因此友元它一定程度上破坏了封装性,需要谨慎使用
#pragma endregion
#pragma region 知识点二 友元函数的概念
//友元函数是一个普通函数,虽然需要在类中声明,但是它不属于类的成员
//通过友元函数可以访问目标类的私有(private)和保护(protected)成员
#pragma endregion
#pragma region 知识点三 友元函数的使用
//规则:
//1.友元函数必须声明在目标类内部
//2.需要在函数前面加上friend关键字
//3.友元函数一般会有一个参数,参数类型为目标类对象
// (因为友元函数并不是类的成员函数,无法通过this指针访问数据)
Lesson11 l;
ChangePrivate(l);
#pragma endregion
#pragma region 知识点四 友元函数传参的几种方式
//1.类型& 参数名
// 这种方式可读可改成员
//2.const 类型& 参数名
// 这种方式比较适用于只读不改,通过引用传递还可以避免不必要的拷贝,并且可以传常量引用和普通引用
//3.类型 参数名
// 不推荐,按值传递,会有拷贝
//注意:允许传多个参数,根据需求来定,但是至少得有目标对象参数才有意义
#pragma endregion
//总结
//友元函数
//本质上是一个特殊的全局函数,该全局函数需要在想要做朋友的类中进行友元声明(前面加上 friend)
//该全局函数就可以在内部访问到对应类对象的私有或保护的成员
//但是一定记住需要将类对象以参数的形式传递进去 因为在内部无法通过this去访问成员
}
11.3 练习题
简要说明友元函数有什么作用
友元函数的主要作用就是允许非成员函数访问类的私有的(private)和保护的(protected)成员
在今后的开发中,比较常见的友元函数应用场景是:
- 日志系统,我们可以利用友元函数获取到私有保护的信息,将它们记录到日志系统的文档中,方便我们之后排查问题
- 调试工具,我们获取到私有保护成员,便于定位问题
- 根据自己的逻辑需求决定是否使用友元函数
实现 friend bool isEqual(const MyClass& obj1, const MyClass& obj2);
友元函数,使其能够比较两个 MyClass
对象的数据是否相等
#pragma once
#include <iostream>
using namespace std;
class A
{
private:
int i = 10;
string str = "韬老师";
public:
float f = 4.0f;
protected:
bool b = false;
public:
A(int i, string str, float f, bool b);
friend bool isEqual(const A& obj1, const A& obj2);
};
#include "A.h"
A::A(int i, string str, float f, bool b)
{
this->i = i;
this->str = str;
this->f = f;
this->b = b;
}
bool isEqual(const A& obj1, const A& obj2)
{
// 判断两个对象中的所有成员变量里面的值是不是都相同
// 如果有指针,应该判断指针中存储的值是否一致
// 如果有数组,应该先判断长度是否一致,再判断里面的元素是否一致
if (obj1.i == obj2.i &&
obj1.str == obj2.str &&
obj1.f == obj2.f &&
obj1.b == obj2.b)
{
return true;
}
return false;
}
A a1 = A(11, "韬老狮", 2.4f, false);
A a2 = A(12, "韬老狮", 2.4f, false);
if (isEqual(a1, a2))
cout << "两个对象的值相等" << endl;
else
cout << "两个对象的值不相等" << endl; // 两个对象的值不相等
尝试将 B
类中的成员函数作为 A
类的友元函数
#pragma once
class A;
class B
{
public:
void GetAInfo(const A& a);
};
#pragma once
#include <iostream>
#include "B.h"
using namespace std;
class A
{
private:
int i = 10;
string str = "韬老师";
public:
float f = 4.0f;
protected:
bool b = false;
public:
A(int i, string str, float f, bool b);
friend bool isEqual(const A& obj1, const A& obj2);
friend void B::GetAInfo(const A& a);
};
#include "B.h"
#include "A.h"
#include <iostream>
using namespace std;
void B::GetAInfo(const A& a)
{
cout << a.i << endl;
cout << a.f << endl;
cout << a.str << endl;
cout << a.b << endl;
}
A a3 = A(666, "韬老狮666", 1.6f, false);
B b;
b.GetAInfo(a3);
// 666
// 1.6
// 韬老狮666
// 0
11.4 练习题代码
A.h
#pragma once
#include <iostream>
#include "B.h"
using namespace std;
class A
{
private:
int i = 10;
string str = "韬老师";
public:
float f = 4.0f;
protected:
bool b = false;
public:
A(int i, string str, float f, bool b);
friend bool isEqual(const A& obj1, const A& obj2);
friend void B::GetAInfo(const A& a);
};
A.cpp
#include "A.h"
A::A(int i, string str, float f, bool b)
{
this->i = i;
this->str = str;
this->f = f;
this->b = b;
}
bool isEqual(const A& obj1, const A& obj2)
{
//判断两个对象中的所有成员变量里面的值 是不是都相同
//如果有指针,应该判断指针中存储的值是否一致
//如果有数组,应该先判断长度是否一致,再判断里面的元素是否一致
if (obj1.i == obj2.i &&
obj1.str == obj2.str &&
obj1.f == obj2.f &&
obj1.b == obj2.b)
{
return true;
}
return false;
}
B.h
#pragma once
class A;
class B
{
public:
void GetAInfo(const A& a);
};
B.cpp
#include "B.h"
#include "A.h"
#include <iostream>
using namespace std;
void B::GetAInfo(const A& a)
{
cout << a.i << endl;
cout << a.f << endl;
cout << a.str << endl;
cout << a.b << endl;
}
Lesson11_练习题.cpp
#include <iostream>
#include "A.h"
#include "B.h"
int main()
{
#pragma region 练习题一
//请简要说明友元函数有什么作用
//
//友元函数的主要作用就是允许非成员函数访问类的私有的(private)和保护的(protected)成员
//在今后的开发中,比较常见的友元函数应用场景是:
//1.日志系统,我们可以利用友元函数获取到私有保护的信息,将他们记录到日志系统的文档中,方便我们之后排查问题
//2.调试工具,我们获取到私有保护成员,我定位问题
//3.根据自己的逻辑需求决定是否使用友元函数
#pragma endregion
#pragma region 练习题二
//请实现 friend bool isEqual(const MyClass & obj1, const MyClass & obj2); 友元函数
//使其能够比较两个 MyClass 对象的数据是否相等:
A a1 = A(11, "韬老狮", 2.4f, false);
A a2 = A(12, "韬老狮", 2.4f, false);
if (isEqual(a1, a2))
cout << "两个对象的值相等" << endl;
else
cout << "两个对象的值不相等" << endl;//两个对象的值不相等
#pragma endregion
#pragma region 练习题三
//尝试将B类中的成员函数作为A类的友元函数
A a3 = A(666, "韬老狮666", 1.6f, false);
B b;
b.GetAInfo(a3);
//666
//1.6
//韬老狮666
//0
#pragma endregion
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com