本文主要是介绍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
两种访问权限:private和others;
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 的某种变种),就按照以下步骤来做:
- 提供一个能高效地交换你的类型的两个 objects 的值的 public(公有)的 swap member function(成员函数)。出于我过一会儿就要解释的动机,这个函数应该永远不会抛出 exception(异常)。
- 在你的 class(类)或 template(模板)所在的同一个 namespace(名字空间)中提供一个 non-member(非成员)的 swap。用它调用你的 swap member function(成员函数)。
- 如果你写了一个 class(类)(不是 class template(类模板)),就为你的 class(类)特化 std::swap。让它也调用你的 swap member function(成员函数)
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 class和interface class解除了接口与实现的耦合关系。
参考: http://blog.csdn.net/skc361/article/details/27977395
这篇关于Effective C++ 摘记(二)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!