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; // 实例化一个house1goodGay(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; // 实例化一个house1GoodGay 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; // 实例化一个house1GoodGay 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;
}
输出:
好基友类正在访问: 客厅
好基友类正在访问: 卧室