Effective C++ 摘记(二)

2024-01-05 08:48
文章标签 c++ effective 摘记

本文主要是介绍Effective C++ 摘记(二),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


(四)、设计与声明

十八、让接口易用而不误用

类型一致性;


或者可以将 Day, Month, Year 定义为 类,来对输入的参数进行限制。

预防客户错误的另一个方法是,限制类型内什么事情可做,什么事情不可做。常见的限制是加上const, 例如 “以const修饰operator*的返回类型”可以防止客户因用户自定义类型而出错。 if (a * b = c) //愿意是 a*b == c, 如果使用const 修饰 运算符* 的返回类型,则会出现编译错误,从而阻止出错。

为了防止出现工厂模式返回的指针,由于客户疏忽没有被释放,使用 构造指资源的函数直接返回智能指针 shared_ptr 的方式。如:

shared_ptr防范跨DLL错误。

十九、设计class犹如设计type

11条准则。

(1)考虑新type的对象如何被构造和销毁

(2)考虑对象的初始化和对象的赋值有什么区别

(3)考虑新type的对象在使用过程中被 pass by value,其拷贝构造函数需要注意什么地方

(4)考虑新type的对象的私有成员的合法值,在构造函数中可以对其进行约束

(5)考虑新type的继承性能,是否需要将析构函数设置为virtual

(6)考虑新的type需要什么类型的转换,以便在类中实现类型转换函数

(7)考虑什么样的函数和操作符对新type而言是合理的

(8)考虑什么样的标准函数应该驳回,将这些函数设置为 private

(9)考虑什么是新type的“未声明接口”。它对效率、异常安全性以及资源运用提供何种保证。

(10)考虑新type是否足够一般化,若是,考虑定义为一个 class template,而不是一个class。

(11)考虑是否需要定义一个新的类型,或者只需要继承一个现成的类就可以了。


二十、常引用参数代替值传递

如果函数参数为 类的对象,则使用 pass-by-const-reference 比 pass-by-value更加有效。

void  test(const SimpleCalculator& c); //将对象引用声明为const可以避免在函数操作中对对象进行修改。

使用 const 对象引用作为参数,可以避免函数形参为父类,而实际传递实参为子类时造成的 参数切割。使得真正传递进函数的参数只是 父类部分。

如果函数参数为 内置数据类型(如 int, double等)或者 STL的迭代器或者 函数对象,则使用pass-by-value 更加有效。


二十一、需要返回对象时候不要返回引用

函数返回不要返回 stack 栈的局部变量或者heap堆的 全局或者静态变量都不要作为引用或者指针返回。

能返回对象,直接返回对象即可。

二十二、成员变量声明为private

两种访问权限:privateothers

protected并不比public封装性好。

二十三、用非成员函数和非友元函数替换成员函数

封装强度和改变强度成反比,因为只影响有限的用户;

类外访问函数封装性好于累内成员函数的封装性,不增加累内私有数据的访问函数的数量;

使用命名空间,使用 non-member函数来提高封装性。


宁可使用 non-member non-friend的函数来代替 member函数,这样可以增加封装性、包裹弹性和机能扩充性。



二十四、参数需要类型转换应使用非成员函数

针对二元运算符重载。

只有当参数位于参数列内,这个参数才是隐式转换的合格参与者。

class Rational {
public:Rational(int numerator = 0 ,int denominator = 1);int numerator() const;int denominator() const;const Rational operator* (const Rational& rhs) const;
private:
}

Rational   r;

Rational result = r* 2; //因为构造函数没有被声明为 explicit,故会进行隐式转换,将2转换为 Rational对象。

Ration result = 2 * r; //不会自动进行隐式转换,因为2 并不处于Rational 对象参数列表的位置。

为了支持交换运算,可以



如果需要对某个函数的所有参数(包括this 指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个 non-member.


二十五、没有异常的swap函数

类外构造特化的swap函数;

不要在swap的时候产生异常。


如果 swap 的缺省实现效率不足(这几乎总是意味着你的 class(类)或 template(模板)使用了 pimpl idiom 的某种变种),就按照以下步骤来做:

  1. 提供一个能高效地交换你的类型的两个 objects 的值的 public(公有)的 swap member function(成员函数)。出于我过一会儿就要解释的动机,这个函数应该永远不会抛出 exception(异常)。
  2. 在你的 class(类)或 template(模板)所在的同一个 namespace(名字空间)中提供一个 non-member(非成员)的 swap。用它调用你的 swap member function(成员函数)。

  3. 如果你写了一个 class(类)(不是 class template(类模板)),就为你的 class(类)特化 std::swap。让它也调用你的 swap member function(成员函数)
  4. namespace std {template<>                       // 全特化void swap<Widget>(Widget& a,     // std::swapWidget& b){a.swap(b);                     // <span style="color: rgb(255, 0, 0); font-family: Arial; line-height: 26px; ">不是 class template(类模板)),就为你的 class(类)特化 </span><span style="color: rgb(255, 0, 0); line-height: 26px; font-family: 'Courier New'; ">std::swap</span>}                                // swap member function
    }

最后,如果你调用 swap,请确保包含一个 using declaration 使 std::swap 在你的函数中可见,然后在调用 swap 时不使用任何 namespace(名字空间)限定。


(五)、实现

二十六、延后变量定义式

不要提前定义,直到使用改变量的前一刻为之;

针对循环内的对象需要根据构造析构与赋值的成本,以及可维护性进行权衡。




二十七、少做转型操作

Base(*this).virFun()只会影响对象的基类部分的数据副本,不会影响对象本身,如果使用指针类型转换则会无穷递归,去掉虚属性则消除类似问题;

用虚函数的特性代替dynamic_cast

尽量使用C++风格的转型。



二十八、避免返回对象内部数据的引用或指针

破坏了封装型;

函数返回对象析构导致空指针。

返回对象内部数据的引用或者指针,有可能会在外部对对象内部的数据进行修改,或者不经意删除对象内部的数据指针,造成内存错误。

如果函数返回 const的对象内部数据的引用,则可以一定程度上避免外部可能造成的对数据的修改。


函数返回对象析构导致空指针。例如:



二十九、异常安全的努力

对象管理资源;

copy-swap实现技术;

异常安全性取决于最弱安全保证的代码。



三十、inline里里外外

隐式:类内直接定义成(友)员函数,


显式:inline关键字;

由于inlining在大多数c++程序中是编译期间的行为,因此inline函数一般置于 头文件中。

template 通常也置于 头文件中,因为它一旦被使用,编译器为了将它具现化,需要知道长什么样子。

如果正在写一个template,且认为该template具体出来的所有函数都需要inlined,就需要将该template设置为inlined。

inline只是一个对编译器的申请,编译器可以拒绝。大多数编译器会拒绝执行太过复杂的inline,如递归等。同时如果函数中含有 virtual,则也会拒绝inline,因为virtual函数的调用是个动态的行为,只有在执行的时候才能够确定。

拒绝:复杂、虚函数、函数指针调用、模板、构造析构函数、影响动态连接或升级、对调试器的挑战(禁用)。

inline函数实际上没有函数地址,因为它被展开到调用处。如果inline函数发生改动,则使用该函数的程序都必须重新编译。


将大多数inlining限制在小型、被频繁调用的函数身上,这可使得日后的调试和二进制升级更容易,也可使得潜在的代码膨胀问题最小化,使得程序速度提升机会最大化。

不要只因为 function template出现在头文件中,就将他们声明为inline。


三十一、降低文件间编译依存关系

能使用引用和指针完成的不使用对象、用class声明代替定义,并提供不同的头文件——程序库文件和类定义头文件;

将接口Person类和实现类 PersonImpl 分离。在这样的设计下,Person的客户就完全与Dates, Addresses以及Person的实现细节分离了,那些classes的实现修改都不需要Person客户的重新编译,此外客户无法看到Person的实现细节,也不可能写出那些“取决于实现细节”的代码,这就是真正的“接口与实现分离”。

编译依存性最小化的本质: 用声明依存性替代实现依存性。让头文件尽可能自我满足。设计策略为:


handle classinterface class解除了接口与实现的耦合关系。

参考: http://blog.csdn.net/skc361/article/details/27977395


这篇关于Effective C++ 摘记(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/572318

相关文章

C++中unordered_set哈希集合的实现

《C++中unordered_set哈希集合的实现》std::unordered_set是C++标准库中的无序关联容器,基于哈希表实现,具有元素唯一性和无序性特点,本文就来详细的介绍一下unorder... 目录一、概述二、头文件与命名空间三、常用方法与示例1. 构造与析构2. 迭代器与遍历3. 容量相关4

C++中悬垂引用(Dangling Reference) 的实现

《C++中悬垂引用(DanglingReference)的实现》C++中的悬垂引用指引用绑定的对象被销毁后引用仍存在的情况,会导致访问无效内存,下面就来详细的介绍一下产生的原因以及如何避免,感兴趣... 目录悬垂引用的产生原因1. 引用绑定到局部变量,变量超出作用域后销毁2. 引用绑定到动态分配的对象,对象

C++读写word文档(.docx)DuckX库的使用详解

《C++读写word文档(.docx)DuckX库的使用详解》DuckX是C++库,用于创建/编辑.docx文件,支持读取文档、添加段落/片段、编辑表格,解决中文乱码需更改编码方案,进阶功能含文本替换... 目录一、基本用法1. 读取文档3. 添加段落4. 添加片段3. 编辑表格二、进阶用法1. 文本替换2

C++中处理文本数据char与string的终极对比指南

《C++中处理文本数据char与string的终极对比指南》在C++编程中char和string是两种用于处理字符数据的类型,但它们在使用方式和功能上有显著的不同,:本文主要介绍C++中处理文本数... 目录1. 基本定义与本质2. 内存管理3. 操作与功能4. 性能特点5. 使用场景6. 相互转换核心区别

C++右移运算符的一个小坑及解决

《C++右移运算符的一个小坑及解决》文章指出右移运算符处理负数时左侧补1导致死循环,与除法行为不同,强调需注意补码机制以正确统计二进制1的个数... 目录我遇到了这么一个www.chinasem.cn函数由此可以看到也很好理解总结我遇到了这么一个函数template<typename T>unsigned

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

深入解析C++ 中std::map内存管理

《深入解析C++中std::map内存管理》文章详解C++std::map内存管理,指出clear()仅删除元素可能不释放底层内存,建议用swap()与空map交换以彻底释放,针对指针类型需手动de... 目录1️、基本清空std::map2️、使用 swap 彻底释放内存3️、map 中存储指针类型的对象

C++ STL-string类底层实现过程

《C++STL-string类底层实现过程》本文实现了一个简易的string类,涵盖动态数组存储、深拷贝机制、迭代器支持、容量调整、字符串修改、运算符重载等功能,模拟标准string核心特性,重点强... 目录实现框架一、默认成员函数1.默认构造函数2.构造函数3.拷贝构造函数(重点)4.赋值运算符重载函数

C++ vector越界问题的完整解决方案

《C++vector越界问题的完整解决方案》在C++开发中,std::vector作为最常用的动态数组容器,其便捷性与性能优势使其成为处理可变长度数据的首选,然而,数组越界访问始终是威胁程序稳定性的... 目录引言一、vector越界的底层原理与危害1.1 越界访问的本质原因1.2 越界访问的实际危害二、基

c++日志库log4cplus快速入门小结

《c++日志库log4cplus快速入门小结》文章浏览阅读1.1w次,点赞9次,收藏44次。本文介绍Log4cplus,一种适用于C++的线程安全日志记录API,提供灵活的日志管理和配置控制。文章涵盖... 目录简介日志等级配置文件使用关于初始化使用示例总结参考资料简介log4j 用于Java,log4c