1.1. C++面向生活
借助一个生活中的例子来理解友元技术:
在生活中,你的家里有 客厅(Public) 和 卧室(Private)。
- 客厅 是开放的,任何来访的客人都可以进入。
- 卧室 是私密的,只有你自己能进去。
- 但是,你可以允许你的好朋友进入你的卧室,他们不会像普通客人那样被拒之门外。
在 C++ 里,类的 私有(private) 和 公共(public) 访问权限就类似这种关系。
如果想让类外的特定函数或类访问私有成员,就需要用到“友元(friend)”技术。
友元的作用是 让某些特殊的函数或类访问另一个类的私有成员,就像你允许好朋友进入你的卧室一样。
C++ 中的 友元(friend) 关键字提供了三种实现方式:
- 全局函数做友元(允许某个函数访问私有成员)
- 类做友元(允许某个类的所有成员函数访问私有成员)
- 成员函数做友元(只允许某个特定的成员函数访问私有成员)
通过友元机制,我们可以在 保持封装性的同时,灵活地控制访问权限,让特定的外部函数或类能够访问私有数据。
1.2. 全局函数做友元
friend void goodGay(Building &building); // goodGay全局函数是 Building好朋友,可以访问Building中私有成员
整体代码:
#include<iostream>
#include<string>
typedef std::string STRING; // 为 std::string 定义一个新的类型别名 STRING。
// 等价于:using STRING = std::string;(C++11引入)
class Building{
private:
STRING m_BedRoom; // 卧室
public:
STRING m_SittingRoom; // 客厅
// 构造函数
Building(){
m_SittingRoom="客厅";
m_BedRoom="卧室";
}
friend void goodGay(Building &building); // goodGay全局函数是 Building好朋友,可以访问Building中私有成员
};
// 全局函数
void goodGay(Building &building){
std::cout<<"好基友全局函数正在访问:"<<building.m_SittingRoom<<std::endl;
std::cout<<"好基友全局函数正在访问:"<<building.m_BedRoom<<std::endl;
}
int main(){
Building house1; // 实例化一个house1
goodGay(house1);
return 0;
}
// 输出:
// 好基友全局函数正在访问:客厅
// 好基友全局函数正在访问:卧室
1.3. 类做友元
friend class GoodGay; // GoodGay类是本类的好朋友,可以访问本类中私有成员
#include<iostream>
#include<string>
typedef std::string STRING; // 为 std::string 定义一个新的类型别名 STRING。
// 等价于:using STRING = std::string;(C++11引入)
class Building{
private:
STRING m_BedRoom; // 卧室
public:
STRING m_SittingRoom; // 客厅
// 构造函数
Building(){
m_SittingRoom="客厅";
m_BedRoom="卧室";
}
friend class GoodGay; // GoodGay类是本类的好朋友,可以访问本类中私有成员
};
class GoodGay{
public:
void visit(Building &building){ // 参观函数 访问Building中的属性
std::cout<<"好基友类正在访问:"<<building.m_SittingRoom<<std::endl;
std::cout<<"好基友类正在访问:"<<building.m_BedRoom<<std::endl;
}
};
int main(){
Building house1; // 实例化一个house1
GoodGay gg1; // 实例化一个好基友
gg1.visit(house1); // 好基友1访问house1中的属性【客厅、卧室】
return 0;
}
// 输出:
// 好基友类正在访问:客厅
// 好基友类正在访问:卧室
1.4. 成员函数做友元
成员函数做友元时,我们可以只让某个特定的成员函数访问类的私有成员,而不是整个类。
语法:
// 只让 GoodGay 类的 visit 函数访问私有成员
friend void GoodGay::visit(Building &building);
具体看下面的代码:
错误示例:
#include<iostream>
#include<string>
typedef std::string STRING; // 为 std::string 定义一个新的类型别名 STRING。
// 等价于:using STRING = std::string;(C++11引入)
class GoodGay; // 提前声明 GoodGay 类
class Building{
private:
STRING m_BedRoom; // 卧室
public:
STRING m_SittingRoom; // 客厅
// 构造函数
Building(){
m_SittingRoom="客厅";
m_BedRoom="卧室";
}
// 只让 GoodGay 类的 visit 函数访问私有成员
friend void GoodGay::visit(Building &building);
};
class GoodGay{
public:
void visit(Building &building){ // 参观函数 访问Building中的属性
std::cout<<"好基友类正在访问:"<<building.m_SittingRoom<<std::endl;
std::cout<<"好基友类正在访问:"<<building.m_BedRoom<<std::endl;
}
};
int main(){
Building house1; // 实例化一个house1
GoodGay gg1; // 实例化一个好基友
gg1.visit(house1); // 好基友1访问house1中的属性【客厅、卧室】
return 0;
}
错误点:
原因: friend
关键字的使用位置不正确。
问题分析
GoodGay
类的定义在Building
之后
- 但是在
Building
里写friend void GoodGay::visit(Building &building);
,编译器此时还 不知道GoodGay
类里有visit
这个成员函数。
解决方案
- 需要先 完整定义
GoodGay
类,然后Building
再声明GoodGay::visit
为友元。
正确示例:
#include <iostream>
#include <string>
typedef std::string STRING; // 定义字符串类型别名
class Building; // 先声明 Building 类
class GoodGay {
public:
void visit(Building &building); // 只有 visit 这个函数是友元
};
class Building {
private:
STRING m_BedRoom; // 卧室(私有成员)
public:
STRING m_SittingRoom; // 客厅(公有成员)
// 构造函数
Building() {
m_SittingRoom = "客厅";
m_BedRoom = "卧室";
}
// 只让 GoodGay 类的 visit 函数访问私有成员
friend void GoodGay::visit(Building &building);
};
// **定义 visit 函数**
void GoodGay::visit(Building &building) {
std::cout << "好基友访问: " << building.m_SittingRoom << std::endl;
std::cout << "好基友访问: " << building.m_BedRoom << std::endl; // 允许访问私有成员
}
int main() {
Building house1; // 实例化一个 Building 对象
GoodGay gg1; // 实例化一个 GoodGay 对象
gg1.visit(house1); // 访问 house1 的成员变量【客厅、卧室】
return 0;
}
输出:
好基友类正在访问: 客厅
好基友类正在访问: 卧室