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++11范围for初始化列表auto decltype详解

《C++11范围for初始化列表autodecltype详解》C++11引入auto类型推导、decltype类型推断、统一列表初始化、范围for循环及智能指针,提升代码简洁性、类型安全与资源管理效... 目录C++11新特性1. 自动类型推导auto1.1 基本语法2. decltype3. 列表初始化3

C++11右值引用与Lambda表达式的使用

《C++11右值引用与Lambda表达式的使用》C++11引入右值引用,实现移动语义提升性能,支持资源转移与完美转发;同时引入Lambda表达式,简化匿名函数定义,通过捕获列表和参数列表灵活处理变量... 目录C++11新特性右值引用和移动语义左值 / 右值常见的左值和右值移动语义移动构造函数移动复制运算符

C++中detach的作用、使用场景及注意事项

《C++中detach的作用、使用场景及注意事项》关于C++中的detach,它主要涉及多线程编程中的线程管理,理解detach的作用、使用场景以及注意事项,对于写出高效、安全的多线程程序至关重要,下... 目录一、什么是join()?它的作用是什么?类比一下:二、join()的作用总结三、join()怎么

C++中全局变量和局部变量的区别

《C++中全局变量和局部变量的区别》本文主要介绍了C++中全局变量和局部变量的区别,全局变量和局部变量在作用域和生命周期上有显著的区别,下面就来介绍一下,感兴趣的可以了解一下... 目录一、全局变量定义生命周期存储位置代码示例输出二、局部变量定义生命周期存储位置代码示例输出三、全局变量和局部变量的区别作用域

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

C++中NULL与nullptr的区别小结

《C++中NULL与nullptr的区别小结》本文介绍了C++编程中NULL与nullptr的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编... 目录C++98空值——NULLC++11空值——nullptr区别对比示例 C++98空值——NUL

C++ Log4cpp跨平台日志库的使用小结

《C++Log4cpp跨平台日志库的使用小结》Log4cpp是c++类库,本文详细介绍了C++日志库log4cpp的使用方法,及设置日志输出格式和优先级,具有一定的参考价值,感兴趣的可以了解一下... 目录一、介绍1. log4cpp的日志方式2.设置日志输出的格式3. 设置日志的输出优先级二、Window

从入门到精通C++11 <chrono> 库特性

《从入门到精通C++11<chrono>库特性》chrono库是C++11中一个非常强大和实用的库,它为时间处理提供了丰富的功能和类型安全的接口,通过本文的介绍,我们了解了chrono库的基本概念... 目录一、引言1.1 为什么需要<chrono>库1.2<chrono>库的基本概念二、时间段(Durat

C++20管道运算符的实现示例

《C++20管道运算符的实现示例》本文简要介绍C++20管道运算符的使用与实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录标准库的管道运算符使用自己实现类似的管道运算符我们不打算介绍太多,因为它实际属于c++20最为重要的