封装文档核心知识点总结(通俗版)
一、文档核心知识点总结(通俗版)
1. 封装:把数据和功能打包成一个类
- 目的:隐藏细节,只暴露必要的接口。比如把 “学生” 的姓名、成绩和相关操作(如显示成绩)封装成
Stu
类。 - 实现:用
private
隐藏数据,public
暴露方法(如show()
、setScore()
)。 - 案例:文档中的
mystring
类封装字符串操作(拷贝、拼接、比较),File
类封装文件读写。
2. 构造函数:对象的 “出生证明”
- 作用:创建对象时自动初始化成员变量。
- 特点:
- 函数名与类名相同,无返回值(连
void
都不写)。 - 可重载(参数不同),如无参构造、有参构造、拷贝构造。
- 自动补全:若不写任何构造函数,编译器会补全无参构造;若手写构造函数,编译器不再补全无参构造(需手动添加)。
- 函数名与类名相同,无返回值(连
- 初始化列表:在构造函数中用
:
初始化成员变量,如Stu(int id, string name) : id(id), name(name) {}
。
3. 析构函数:对象的 “清理工”
- 作用:对象生命周期结束时自动释放资源(如堆内存、文件句柄)。
- 特点:
- 函数名是类名前加
~
(如~Stu()
),无参数、无返回值。 - 若不手写,编译器自动补全空析构函数;若类中有动态资源(如
new
/malloc
),必须手写释放逻辑。
- 函数名是类名前加
4. 运算符重载:让自定义类支持 “特殊操作”
- 作用:让自定义对象能使用常见运算符(如
+
、<<
、[]
)。 - 案例:
mystring
类重载+
实现字符串拼接,重载<<
实现cout << str
。Complex
类重载+
、-
实现复数运算。
- 注意:
- 流运算符
<<
/>>
需定义为类的友元函数(因为要访问私有成员,且属于ostream
/istream
类的操作)。 - 下标运算符
[]
通常返回引用(如char& operator[](int idx)
),支持读写。
- 流运算符
5. 引用:变量的 “别名”
- 作用:给变量取别名,本质是同一内存地址的不同名字。
- 特点:
- 必须初始化(如
int a=10; int& b=a;
),一旦绑定不可更改。 - 常用于函数参数(如
void func(int& x)
),避免拷贝构造,提高效率。
- 必须初始化(如
6. 静态成员:属于类而非对象的数据 / 函数
- 静态变量:
- 所有对象共享,存储在静态存储区(如银行类的利率
static double rate
)。 - 需在类外初始化(如
double Bank::rate = 3.15;
)。
- 所有对象共享,存储在静态存储区(如银行类的利率
- 静态函数:
- 无
this
指针,只能访问静态成员。 - 可通过
类名::函数名
直接调用(如Bank::getRate()
)。
- 无
7. 单例模式:保证类只有一个实例
- 作用:确保一个类全局只有一个对象(如文件管理器、数据库连接池)。
- 分类:
- 懒汉模式:首次使用时创建对象,线程不安全(需加锁)。
- 饿汉模式:程序启动时创建对象,线程安全(简单但浪费内存)。
- 实现关键:私有化构造函数,通过静态成员函数返回唯一实例。
8. 友元函数 / 类:打破封装的 “特殊权限”
- 作用:允许外部函数 / 类访问当前类的私有成员。
- 案例:
friend ostream& operator<<(ostream&, const Complex&)
:让cout << complex
能访问Complex
的私有成员。- 友元类
B
的所有函数可访问类A
的私有成员(在A
中声明friend class B;
)。
9. lambda 表达式:匿名的 “一次性” 函数
- 作用:无需命名,直接定义并使用函数(常用于回调函数、排序比较等)。
- 语法:
cpp
[捕获列表](参数列表) mutable -> 返回类型 { 函数体 }
- 捕获列表:按值
[=]
或引用[&]
捕获外部变量。 - mutable:允许修改按值捕获的变量。
- 返回类型:可省略,自动推导。
- 捕获列表:按值
- 案例:
sort(arr.begin(), arr.end(), [](int a, int b){ return a > b; });
(降序排序)。
二、作业解析:封装msgqueue
类
作业要求
实现一个消息队列类msgqueue
,支持:
msgqueue msg("文件名")
:根据文件名创建消息队列。- 析构函数销毁消息队列。
msg[1] << "hello"
:将数据写入 1 频道。msg[1] >> str
:从 1 频道读取数据到str
。
实现思路
-
数据结构:
- 用
string filename
存储文件名。 - 用
map<int, queue<string>> channels
管理各频道的消息队列(频道号为键,消息队列为值)。
- 用
-
构造与析构:
- 构造函数:根据文件名初始化(可能关联系统消息队列,如使用
msgget
创建 IPC 消息队列)。 - 析构函数:销毁消息队列(如
msgctl(IPC_RMID)
)。
- 构造函数:根据文件名初始化(可能关联系统消息队列,如使用
-
运算符重载:
operator[]
:根据频道号返回对应频道对象(内部类Channel
)。Channel
类重载<<
和>>
:实现消息的入队和出队。
-
系统调用(简化版,仅模拟逻辑):
- 实际项目中需使用系统 API(如 POSIX 消息队列
mq_open
等),此处用map
模拟频道存储。
- 实际项目中需使用系统 API(如 POSIX 消息队列
参考代码(简化模拟)
cpp
#include <iostream>
#include <map>
#include <queue>
#include <string>
using namespace std;// 内部类:代表一个消息频道
class Channel {
public:void write(const string& data) {q.push(data); // 模拟入队}void read(string& data) {if (!q.empty()) {data = q.front(); // 模拟出队q.pop();}}
private:queue<string> q; // 频道内的消息队列
};class msgqueue {
public:explicit msgqueue(const string& filename) : filename_(filename) {// 实际此处应调用系统函数创建消息队列(如msgget)cout << "创建消息队列,文件名:" << filename_ << endl;}~msgqueue() {// 实际此处应销毁消息队列(如msgctl(IPC_RMID))cout << "销毁消息队列,文件名:" << filename_ << endl;channels_.clear();}// 重载[]获取频道Channel& operator[](int channel_id) {return channels_[channel_id]; // 自动创建不存在的频道}private:string filename_; // 消息队列关联的文件名map<int, Channel> channels_; // 频道号 -> 频道对象
};int main() {msgqueue msg("msgqueue.txt");msg[1] << "hello"; // 写入1频道msg[1] << "world";string str;msg[1] >> str; // 读取1频道cout << "读取到:" << str << endl; // 输出:helloreturn 0;
}
关键点说明
- 频道管理:用
map<int, Channel>
存储各频道,operator[]
自动创建不存在的频道。 - 消息读写:
Channel
类用队列模拟消息存储,<<
入队,>>
出队(简化逻辑,实际需处理线程安全、序列化等)。 - 系统交互:实际项目中需替换为真实的系统消息队列 API(如 Linux 的
msgget
、msgsnd
、msgrcv
),并处理消息类型和权限。