自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(59)
  • 收藏
  • 关注

原创 Item31:将文件间的编译依存关系降至最低

C++中,很多场景只需类的声明(class declaration)即可,无需完整定义(class definition)。声明指针或引用(无需知道类的大小和成员);声明接受或返回该类的函数(参数和返回值为指针/引用时)。改进方案:用前置声明替代#include,仅在源文件中包含必要的定义。// Person.h(改进后)class Date;

2025-08-11 12:44:06 883

原创 Item10:令 operator = 返回一个 reference to *this

Effective C++》Item10的核心,是通过"返回reference to *this"这一简单约定,实现自定义类型与C++语言原生行为的对齐。它不仅确保了连续赋值、链式操作等常见场景的有效性,更重要的是维持了代码的直觉性与一致性——让用户无需为自定义类型的赋值行为额外学习特殊规则。无论是基础类、跨类型交互还是继承体系,遵循这一惯例都能显著提升代码的可用性与可维护性。这正是C++编程中"惯例即规范"的体现:看似微小的设计选择,却能在大规模代码中产生深远的一致性价值。

2025-08-03 09:45:52 943

原创 Item9:绝不在构造和析构过程中调用 virtual 函数

构造与析构阶段的对象动态类型不完整,此时调用虚函数无法实现预期的多态行为,可能导致逻辑错误或资源问题。用构造函数参数传递必要信息,避免构造时调用虚函数;采用工厂模式,在对象完全构造后再执行依赖多态的初始化逻辑。遵循这一准则,可有效避免因对象生命周期阶段特性导致的隐蔽错误,提升代码的健壮性。

2025-08-03 09:21:31 952

原创 Item 8:别让异常逃离析构函数

析构函数的稳定性是程序资源安全与流程可靠的底线。异常逃离析构函数可能引发资源泄露、流程混乱甚至程序崩溃,其风险远大于“吞掉异常”的潜在问题。尽量避免在析构函数中执行可能抛异常的操作;若无法避免,必须在析构函数内部用try-catch拦截并处理;非必要的风险操作应转移到用户显式调用的函数中,让用户自主处理异常。唯有如此,才能确保析构函数履行“清理者”职责时的可靠性,为程序健壮性筑牢防线。

2025-08-03 09:21:15 975

原创 item 7: 声明多态基类的析构函数为虚函数

虚析构函数的核心价值在于保障多态场景下的资源安全释放。其底层依赖虚函数表机制,通过将析构函数纳入动态绑定流程,确保派生类析构能被正确调用。若类是多态基类(含虚函数,用于通过基类接口管理派生对象),必须声明虚析构函数;若非多态类或不打算作为基类,不应声明虚析构函数,避免不必要的内存开销;警惕继承非虚析构的类(如STL容器),此类继承本质上违背了原类的设计意图。理解虚析构函数的原理与边界,是写出健壮C++代码的重要基础。

2025-08-03 09:20:58 1075

原创 Item 5 :看透编译器自动生成的特殊成员函数

编译器自动生成的4个特殊函数是把双刃剑:它们简化了代码编写,但在涉及动态资源、引用、const成员或继承时,可能隐藏致命风险。默认构造函数:仅当类无任何构造函数时生成;若成员无默认构造,则无法生成。析构函数:总会生成(非虚);管理资源时必须手动实现,若类可能被继承,需定义为虚函数。拷贝构造与拷贝赋值:生成时执行浅拷贝;含动态资源时必须手动实现深拷贝;含引用/const成员时,拷贝赋值运算符无法生成。理解这些规则,才能在“依赖编译器”和“手动实现”之间做出正确选择,写出安全且高效的C++代码。

2025-08-03 09:20:30 774

原创 Item 6:若不想使用编译器自动生成的函数,就该明确拒绝​

Item 6的核心思想是显式管理编译器自动生成的函数。通过private声明(旧式)或=delete(现代)主动拒绝不需要的函数,可避免意外行为并增强代码可读性。在C++11及以后的代码中,=delete是更优选择,它提供了更严格的编译期检查和更清晰的语义表达。这一条款的本质是:在类设计中,明确性优于隐式性。通过显式控制特殊成员函数,我们能构建更健壮、更符合设计意图的类接口。

2025-08-03 09:20:06 1102

原创 item 4:确保对象被使用前已经被初始化

内置类型:函数内的内置类型(含指针)必须显式初始化,不依赖全局变量的默认零值;类成员:优先使用初始化列表(效率更高),这是const成员、引用成员和无默认构造函数成员的唯一选择;初始化顺序:类成员的初始化顺序由声明顺序决定,与初始化列表顺序无关;静态对象:跨编译单元使用时,用“函数内静态对象”模式避免初始化顺序不可控问题。遵循这些准则,可有效规避C++初始化相关的未定义行为,让代码更健壮、可预测。

2025-08-02 19:37:01 612

原创 Item55:让自己熟悉Boost

Boost是C++开发者的“扩展工具箱”,其价值不仅在于提供实用组件,更在于传递现代C++的设计理念。Boost填补了标准库空白,覆盖从基础工具到系统编程的全领域;大量组件已成为C++标准的一部分,学习Boost等于提前掌握标准特性;优先学习与业务相关的库(如Boost.Asio),按需引入。对于追求高效、高质量C++开发的开发者而言,熟悉Boost是提升技术栈的关键一步。Boost不仅是库的集合,更是C++最佳实践的百科全书。

2025-08-02 17:07:39 831

原创 Item54:让自己熟悉包括TR1在内的标准程序库

提供经过验证的正确组件,减少错误;优化的性能,优于大多数手动实现;跨平台兼容性,降低移植成本。TR1引入的shared_ptrfunction等组件,解决了传统STL的关键痛点,成为现代C++开发的基石。花时间熟悉标准库,比重复造轮子更有价值。充分利用标准库不仅能提高开发效率,还能让代码更健壮、更易维护。

2025-08-02 17:07:36 919

原创 Item53:不要忽略异常

忽略异常(空catch块)是C++程序中隐藏的“定时炸弹”,其危害包括资源泄漏、数据不一致和错误扩散。不忽略:空catch块仅在极端特殊场景下使用(且需注释说明原因);不掩盖:无法处理时用throw;重新抛出,或包装后传播;要一致:处理异常后必须确保程序状态恢复一致,避免后续操作出错。异常是程序的“求救信号”,忽略信号只会让问题恶化。负责任的异常处理是编写健壮、可维护代码的基础。

2025-08-02 17:07:16 561

原创 Item52:避免过度使用异常规格(Exception Specifications)

异常规格的设计从C++98的动态异常规格演进到C++11的noexcept动态异常规格因运行时开销和维护问题被弃用,不应在新代码中使用;noexcept通过静态保证提供接口契约,无运行时开销,是现代C++的首选;noexcept应仅用于确实不抛异常的函数(如析构、swap),过度使用会导致程序脆弱性增加。记住:noexcept的价值在于“明确不抛异常的承诺”,而非“强制不抛异常”。合理使用可提升代码的异常安全性和性能,滥用则可能引入难以调试的崩溃。

2025-08-02 17:07:12 847

原创 Item51:了解什么是异常安全(Exception Safety)

异常安全分三个级别:基本保证(状态合法)、强保证(状态回滚)、不抛保证(绝不失败);异常不安全的主要风险是资源泄漏和数据破坏;实现异常安全的核心手段是RAII(防泄漏)和拷贝再交换(强保证);析构函数必须提供不抛保证,否则可能导致程序终止。设计函数时需明确其提供的异常安全级别,并通过RAII和无异常提交等技术实现。异常安全并非可有可无的特性,而是生产级代码必须满足的基本要求。

2025-08-02 17:07:08 858

原创 Item50:了解C++的新型转换方式

编译期确定的显式转换,适用于非多态类型;const_cast:唯一修改const属性的转换,使用需极度谨慎;:多态类型的安全下行转换,依赖运行时检查;:低级内存重新解释,可移植性差,仅用于底层操作。优先使用新型转换而非C风格转换,它们能使代码意图更清晰,错误更易被编译器捕获,从而显著提升代码质量和可维护性。

2025-08-02 17:07:02 634

原创 Item49:了解new-handler的行为

new-handler在分配失败时被调用,职责是释放内存、切换handler或终止程序;设计时必须避免无限循环:无法处理时务必终止程序;类专属new-handler可通过重载和线程局部存储实现,适合定制化需求;与nothrow new相比,new-handler更适合需要主动补救的场景。正确使用new-handler能显著提升程序在低内存环境下的健壮性,尤其对于服务器、数据库等长运行程序至关重要。

2025-08-02 17:06:52 747

原创 Item48:认识template元编程的威力

将运行时工作转移到编译期,实现“零成本抽象”;提供强大的类型操作能力,是泛型编程的基础;虽有语法复杂、编译慢等局限,但在性能敏感和类型安全要求高的场景中不可替代。理解TMP不仅能帮助你更好地使用标准库,还能让你编写更高效、更通用的代码。TMP的目标不是炫技,而是用编译期的代价换取运行时的最优性能和安全性。

2025-08-02 17:06:39 603

原创 Item47:使用traits classes提供类型信息

主模板声明需要萃取的特性(如category迭代器类别),并给出默认值(通常适用于大多数类型)。// 迭代器traits主模板// 默认迭代器类别(假设为输入迭代器)// 可序列化特性traits// 默认不可序列化// 为可序列化类型特化// 仅处理可序列化类型的函数// 序列化逻辑实现编译期的类型特性查询,为模板提供决策依据;通过模板特化支持灵活的特性定制,适配不同类型;是STL算法与迭代器协作的基础,也是现代C++元编程的重要工具。

2025-08-02 17:06:35 744

原创 Item46:需要类型转换时请为模板定义非成员函数

当模板类需要支持参数间的隐式类型转换时,非成员函数是唯一有效的方案允许编译器对所有参数应用隐式转换,再推导模板参数;支持交换律(如a + b与b + a均有效);可通过全特化针对特定类型提供定制实现。模板类的二元运算符(如)几乎都应定义为非成员函数,以确保类型转换的灵活性和代码的直观性。

2025-08-02 17:06:26 505

原创 Item45:运用成员函数模板接受所有兼容类型

自动生成接受不同类型的成员函数版本,支持灵活的类型转换;特别适用于智能指针、容器等需要跨类型操作的场景;需注意与默认成员函数的协作,必要时显式声明以避免冲突。明确模板的适用范围(如仅接受兼容类型),并处理好与默认函数的关系。这一机制极大提升了C++代码的泛化能力和类型安全性。

2025-08-02 17:06:23 524

原创 Item44:将与参数无关的代码抽离templates

模板的代码膨胀并非不可避免,核心在于区分与参数相关和无关的逻辑与参数无关的代码(如通用算法、内存管理)应抽离为非模板实体;与参数相关的代码(如类型特定操作)保留在模板中。这种分离不仅减少目标代码大小,还提高了代码复用率和维护性。好的模板设计应“只模板化必要的部分”,避免将模板作为“万能复用工具”而忽视代码冗余问题。

2025-08-02 17:06:06 639

原创 Item43:学习处理模板化基类内的名称

模板化基类的名称在派生类中不可见,是C++模板名称查找规则的必然结果。this->:简洁,适用于非静态成员的常规访问;using声明:适合批量引入成员,避免名称遮蔽;显式基类作用域:明确且强制,适用于静态成员或抑制多态的场景。在模板派生类中访问基类成员时,必须通过上述方法明确告知编译器名称来源,否则会因名称查找失败导致编译错误。这一规则是模板继承中确保代码正确性的基础。

2025-08-02 17:06:00 301

原创 Item42:了解typename的双重意义

依赖类型名:类型名依赖于模板参数,如(MemberType是否为类型取决于T);嵌套类型名:依赖类型名被嵌套在类内部,如;嵌套依赖类型名:同时满足以上两点的类型名,即“依赖于模板参数的嵌套类型”。public:// 若T包含嵌套类型Iterator,则T::Iterator是嵌套依赖类型名// 必须用typename标识typename模板参数声明typename与class均可用于声明类型参数,优先使用typename以增强可读性;嵌套依赖类型名:必须用typename。

2025-08-02 17:05:46 972

原创 Item41:了解隐式接口和编译期多态

显式接口:由类声明定义,支撑运行期多态(虚函数),适合“类型层级明确”的场景;隐式接口:由表达式约束定义,支撑编译期多态(模板),适合“跨类型通用算法”的场景。理解二者的差异与适用场景,才能在类设计与模板编程中做出合理选择。接口的本质是“交互契约”,多态的本质是“行为适配”,实现方式的选择应服务于具体需求。

2025-08-01 10:06:56 752

原创 Item40:明智而审慎地使用多重继承

风险:成员歧义、菱形继承、构造函数复杂;价值:组合多个接口、复用多个实现类的功能。优先通过复合+单继承解决问题,多重继承作为最后选择;若使用,尽量只继承一个实现类,其余为接口类;用虚继承解决菱形问题,但需注意构造函数和性能开销;显式指定基类成员以避免歧义调用。多重继承的核心是“必要时谨慎使用”,而非“禁止使用”。只有充分理解其风险并采取规避措施,才能发挥其价值。

2025-08-01 10:05:42 602

原创 Item39:明智而审慎地使用private继承

private继承是一种“有限且危险”的工具,其核心价值在于:在必须访问基类protected成员或重写虚函数时,提供比复合更直接的实现复用方式。但它的耦合性高于复合,语义模糊,应作为“最后的选择”。能用复合解决的问题,就不要用private继承。仅当复合无法满足需求(访问protected成员或重写虚函数)时,才谨慎使用private继承,并通过清晰的注释和测试降低维护风险。

2025-08-01 10:04:51 897

原创 Item38:通过复合塑模出has-a或is-implemented-in-terms-of关系

避免继承带来的接口污染和语义混淆;降低类之间的耦合,提高代码复用的灵活性;清晰区分“对象组成”与“实现依赖”两种语义。继承只用于is-a关系,复合用于所有其他关系。当面临“继承还是复合”的选择时,优先考虑复合——它几乎总是更安全、更灵活的方案。

2025-08-01 10:03:42 1001

原创 Item37:绝不重新定义继承而来的缺省参数值

/ 基类文档声明:draw()默认使用红色public:// 派生类暗中修改默认参数,与基类文档冲突public:开发者依据基类文档调用draw()时,会默认使用红色,但实际行为因派生类而改变,导致代码行为与文档不符。虚函数与缺省参数的绑定方式差异,决定了派生类绝不能重新定义继承而来的缺省参数值。虚函数动态绑定,缺省参数静态绑定,二者混合使用会导致行为不一致;缺省参数是基类接口契约的一部分,派生类修改会破坏继承逻辑。

2025-08-01 10:01:45 951

原创 Item36:绝不重新定义继承而来的non-virtual函数

根据is-a原则,派生类对象应能被无差别地当作基类对象使用。public:void eat() const { // 非虚函数:所有动物的通用进食行为public:void eat() const { // 错误:重新定义非虚函数// 函数预期:所有Animal(包括派生类)调用eat()时表现一致// 传入Dog时,实际调用Animal::eat,与预期矛盾调用会输出,但Dog的实际实现被忽略。这使得派生类无法正确表达自身特性,违背了继承的设计初衷。代表基类的不变行为。

2025-08-01 10:00:24 620

原创 Item35:考虑virtual函数以外的其他选择

虚函数是多态的基础,但并非“银弹”。NVI模式强化基类对接口的控制;函数指针/实现行为与类型的彻底解耦;策略模式适合复杂行为的封装与扩展。多态的核心是“行为分离”,而非“必须用虚函数”。根据场景选择最匹配的方案,才能设计出灵活、易维护的代码。

2025-08-01 09:59:38 442

原创 Item34:区分接口继承与实现继承

只继承接口,确保派生类履行契约;继承接口+默认实现,平衡复用与灵活性;继承接口+强制实现,保证基类行为的稳定性。错误的继承类型选择会导致接口失效、多态异常或逻辑漏洞。设计类时,先明确“派生类需要继承什么”,再选择对应的函数类型。

2025-08-01 09:58:50 852

原创 Item33:避免hide继承而来的名称

派生类同名成员会无条件屏蔽基类所有同名实体,与参数列表无关。需完整继承基类重载族:用using声明引入基类名称;需部分暴露基类成员:在派生类中显式转发;避免名称冲突:优先使用不同名称命名派生类成员。继承的核心是行为扩展而非名称覆盖,合理管理继承而来的名称是维持代码可读性和正确性的关键。

2025-08-01 09:57:14 352

原创 Item32:确定你的public继承塑模出is-a关系

public继承不是“代码复用的工具”,而是“语义关系的声明”。只有当派生类真正是基类的“一种”(is-a),且能完全遵守基类的行为契约时,才能使用public继承。违反这一原则会导致代码逻辑混乱、多态失效,甚至引入难以调试的错误。继承的核心是“行为兼容”,而非“属性相似”。

2025-08-01 09:55:10 718

原创 Item30:透彻了解 inlining 的里里外外

inline是C++中平衡性能与代码结构的重要机制,但其效果高度依赖使用场景。仅对小型、简单、频繁调用的函数使用inline,避免代码膨胀;认识到inline是编译器的“建议”,而非强制指令;通过profiling验证优化效果,不盲目依赖inline提升性能;警惕构造函数、析构函数及虚函数中的inline陷阱。合理使用inline可显著提升程序效率,而滥用则会导致维护成本上升和潜在的性能问题。理解其本质与限制,是写出高效且健壮代码的关键。

2025-08-01 09:45:19 749

原创 Item29:为异常安全而努力是值得的

不泄露资源:即使异常抛出,已分配的资源(如内存、文件句柄、锁)必须正确释放。不破坏数据:异常发生后,对象状态需保持一致(要么完全成功,要么回到初始状态,避免中间态)。资源泄露:如new分配的内存未被delete,文件未关闭导致句柄耗尽。数据损坏:对象成员变量处于部分修改状态,后续操作依赖错误数据。逻辑混乱:如锁未释放导致死锁,事务未回滚导致数据不一致。用RAII彻底避免资源泄露(依赖析构函数noexcept对核心功能实现“强烈保证”(通过Copy-and-Swap);

2025-08-01 09:43:34 742

原创 Item28:避免返回handles指向对象内部成分

返回指向对象内部的handles会破坏封装性、引入悬垂引用风险,并可能导致不可预期的状态修改。优先返回数据拷贝或const引用,避免暴露内部可修改的handles;若需返回指针,使用智能指针(如weak_ptr)管理生命周期,明确所有权;通过类接口提供必要操作,而非直接暴露内部结构,确保封装性。遵循这一原则可显著提升代码的健壮性和可维护性,减少因外部不当访问导致的逻辑错误。

2025-08-01 09:41:55 976

原创 Item12:复制对象时勿忘其每一个成分

复制所有成员:包括当前类的所有成员变量,无论访问权限。复制基类成分:通过调用基类的复制构造函数和复制赋值运算符,确保继承体系中的所有成分被正确复制。利用编译器生成的函数:简单类优先使用=default,避免手动实现遗漏成分。资源管理类的深拷贝:动态资源需完整复制,避免浅拷贝风险。遵循这一原则可确保对象复制后状态完整一致,是编写可靠C++代码的基础。

2025-07-29 14:44:33 554

原创 Item27:尽量少做转型动作

优先避免转型:通过多态、工厂模式等设计减少转型需求。慎用:仅在必要时使用,避免高频场景,考虑缓存转型结果。禁止:除非与硬件交互等底层操作,否则坚决不用。利用C++11特性auto、智能指针等减少显式转型,提升类型安全性。减少转型的核心是“尊重类型系统”——让编译器的类型检查帮助发现错误,而非通过转型绕过检查。

2025-07-29 12:47:21 855

原创 Item26:尽可能延后变量定义式的出现时间

不必要的构造与析构开销:即使变量在某些路径下未被使用,仍会执行构造和析构。作用域扩大:变量生命周期超出实际需要,增加命名冲突风险。初始化与赋值混淆:先定义后赋值会导致两次操作(构造+赋值),不如直接初始化高效。// 反例:过早定义变量// 过早定义// encrypted在此路径下未使用,但已构造// 实际使用点// 赋值操作(非初始化)// 加密函数} // encrypted析构// 正例:延后定义并直接初始化// 直接初始化} // 仅在必要时构造和析构。

2025-07-29 12:44:58 314

原创 Item25:考虑写出一个不抛异常的swap函数

提供成员函数swap:对于管理动态资源的类,通过交换指针实现高效、不抛异常的swap。提供non-member swap:在类的命名空间内定义调用成员swap的non-member版本,确保ADL规则生效。利用C++11移动语义:对于支持移动的类型,std::swap已足够高效;确保移动操作标记为noexcept。异常安全编程:在需要强异常保证的代码中,依赖不抛异常的swap操作。通过合理实现swap,可显著提升代码的性能和可靠性,尤其在资源管理和异常处理场景中。

2025-07-29 12:43:19 333

原创 Item24:若所有参数皆需类型转换,请为此采用non-member函数

当运算符需要对所有参数进行隐式类型转换时,必须将其实现为non-member函数。这一规则确保了运算符两侧参数的一致性,同时保持了类型系统的灵活性。C++11的explicit构造函数和显式转换机制进一步增强了类型安全性,使开发者能够在保持转换能力的同时避免潜在风险。

2025-07-29 12:41:13 783

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除