news 2026/6/3 7:00:14

【C++ 从基础到项目实战】C++(九):友元与设计模式初探——打破封装的艺术

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C++ 从基础到项目实战】C++(九):友元与设计模式初探——打破封装的艺术

📌 阅读时长:22分钟 | 关键词:C++、友元函数、友元类、friend、单例模式、设计模式

引言

前面文章中我们反复强调"封装"——用private把数据藏起来。但有时,我们确实需要给某些外部的函数或类开一扇"后门",让它们能访问私有成员。这扇门就叫友元(friend)。文章最后,我们还会用静态成员 + 友元的知识,实现第一个设计模式——单例模式

一、友元函数:类的"特许通行证"

1.1 什么是友元函数?

在类中用friend声明的普通函数,可以访问该类的私有成员:

classBox{private:doublewidth;public:Box(doublew):width(w){}friendvoidprintWidth(Box&b);// 声明友元函数};// 定义友元函数(在类外部)voidprintWidth(Box&b){// 可以直接访问私有成员!std::cout<<"宽度:"<<b.width<<std::endl;}intmain(){Boxbox(10.0);printWidth(box);// 输出:宽度:10}

1.2 友元函数可以修改私有成员

classCuboid{private:doublelength,width,height;public:Cuboid(doublel,doublew,doubleh):length(l),width(w),height(h){}friendvoidupdateDimensions(Cuboid&c,doublel,doublew,doubleh);frienddoublecalculateVolume(constCuboid&c);};voidupdateDimensions(Cuboid&c,doublel,doublew,doubleh){c.length=l;c.width=w;c.height=h;// 直接修改私有成员}doublecalculateVolume(constCuboid&c){returnc.length*c.width*c.height;// 直接读取私有成员}

1.3 友元函数的要点

特性说明
不是成员函数没有this指针,通过参数传递对象
声明位置类内任意位置(public/protected/private 都行)
访问权限可访问该类的所有成员(public + protected + private)
集中声明建议将友元声明集中在类的开头或结尾,便于代码维护

1.4 友元的优缺点

优点缺点
实现运算符重载 (<<,>>) 的自然语法破坏封装性
多类协作时访问对方私有成员增加类之间的耦合度
简化某些特殊操作的代码滥用后代码难以维护

二、友元类:整班都是 VIP

声明一个类为另一个类的友元,则该类的所有成员函数都能访问对方的私有成员:

classCircle{private:doubleradius;public:Circle(doubler):radius(r){}friendclassGeometry;// Geometry 是 Circle 的友元类};classGeometry{public:doublecalcArea(constCircle&c){return3.14159*c.radius*c.radius;// 访问私有成员}voidsetRadius(Circle&c,doubler){c.radius=r;// 修改私有成员}};intmain(){Circlec(5.0);Geometry g;std::cout<<g.calcArea(c)<<std::endl;// 78.5397g.setRadius(c,8.0);std::cout<<g.calcArea(c)<<std::endl;// 201.062}

友元关系的三大特性

// 1. 单向性:A 是 B 的友元 ≠ B 是 A 的友元classA{friendclassB;};// B 能访问 A 的私有// A 不能访问 B 的私有 ← 除非 B 也 friend class A// 2. 非传递性:A→B 是友元,B→C 是友元 ≠ A→C 是友元// 3. 不能被继承:父类的友元不能自动访问子类的新增私有成员

三、友元 + 运算符重载

友元最经典的用法是重载流运算符<<>>

classCuboid{private:doublelength,width,height;public:Cuboid(doublel,doublew,doubleh):length(l),width(w),height(h){}// 友元重载 + 运算符friendCuboidoperator+(constCuboid&a,constCuboid&b);// 友元重载 << 运算符friendstd::ostream&operator<<(std::ostream&os,constCuboid&c);};Cuboidoperator+(constCuboid&a,constCuboid&b){returnCuboid(a.length+b.length,std::max(a.width,b.width),std::max(a.height,b.height));}std::ostream&operator<<(std::ostream&os,constCuboid&c){os<<"Cuboid("<<c.length<<", "<<c.width<<", "<<c.height<<")";returnos;}intmain(){Cuboidc1(3,2,1),c2(4,1,5);std::cout<<c1+c2<<std::endl;// Cuboid(7, 2, 5)}

四、设计模式初探:单例模式(Singleton)

学完了静态成员 + 私有构造函数 + 友元,我们已经具备了实现单例模式的能力——一个全局只能存在一个实例的类。

4.1 为什么需要单例?

  • 日志记录器:全局共用一个
  • 数据库连接池:避免重复创建连接
  • 配置管理器:全局一份配置

4.2 基本实现

classSingleton{private:staticSingleton*instance;// 静态指针,保存唯一实例Singleton(){}// ① 构造函数是私有的!外部不能 newSingleton(constSingleton&)=delete;// ② 禁止拷贝Singleton&operator=(constSingleton&)=delete;// ③ 禁止赋值public:staticSingleton*getInstance(){// ④ 静态方法获取唯一实例if(instance==nullptr)instance=newSingleton();returninstance;}voiddoSomething(){std::cout<<"单例模式工作中..."<<std::endl;}};Singleton*Singleton::instance=nullptr;// 静态成员定义intmain(){// Singleton s; ❌ 构造函数是私有的Singleton*s1=Singleton::getInstance();Singleton*s2=Singleton::getInstance();std::cout<<(s1==s2)<<std::endl;// 1 — 同一个对象!s1->doSomething();}

4.3 C++11 线程安全版(Meyer’s Singleton)

classSingleton{public:// C++11 保证局部静态变量的初始化是线程安全的!staticSingleton&getInstance(){staticSingleton instance;returninstance;}voiddoSomething(){std::cout<<"线程安全单例"<<std::endl;}private:Singleton()=default;Singleton(constSingleton&)=delete;Singleton&operator=(constSingleton&)=delete;};// 使用:Singleton::getInstance().doSomething();
实现方式线程安全内存释放代码量
原始指针 + new不自动
Meyer’s Singleton✅ (C++11)自动极少

💡 日常开发中直接用Meyer’s Singleton,简单安全,无需手动 delete。

4.4 设计模式思维

模式核心思想C++ 实现关键
单例 (Singleton)全局唯一实例私有构造函数 + 静态变量
工厂 (Factory)集中创建对象静态方法 + 返回指针/智能指针
观察者 (Observer)一对多通知虚函数 + 指针列表

设计模式不是银弹,但了解它们能让你在面对常见问题时不再"重新发明轮子"。

小结

序号知识点一句话总结
1友元函数friend 声明的外部函数可访问类私有成员,常用于运算符重载
2友元类整个类都能访问对方的私有成员
3友元三大特性单向、非传递、不能被继承
4友元利弊方便但破封装,只在确实需要时用
5单例模式私有构造 + 静态变量 → 全局唯一实例
6Meyer’s SingletonC++11 线程安全,局部静态变量自动清理,推荐首选

至此,面向对象核心模块全部完成!下一篇文章,我们将进入模板编程——用泛型编程写出类型无关的高复用代码。


本文是「C++ 从基础到项目实战」系列的第 9 篇。关注我,不错过后续更新。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/3 6:54:06

构建个人知识复利系统:从信息处理到可复用资产的技术实践

1. 项目概述&#xff1a;从“银质红利”看个人研究的长期价值最近在整理过往的研究笔记和项目资料时&#xff0c;一个老话题——“研究的长期价值”——又浮现在我的脑海里。这让我想起了多年前读过的一篇关于“Fitzgibbon’s Research Reaps Silver Dividend”的案例&#xff…

作者头像 李华
网站建设 2026/6/3 6:54:05

事件相机视觉运动策略:从数据表征到机器人控制实战

1. 项目概述&#xff1a;从事件相机到自主系统的视觉运动策略学习最近几年&#xff0c;在机器人、无人机和自动驾驶这些需要实时感知与决策的领域&#xff0c;传统基于帧的相机&#xff08;比如我们手机上的摄像头&#xff09;遇到了一个明显的瓶颈&#xff1a;延迟和功耗。想象…

作者头像 李华