Item 21: Don’t to return a reference when you must return an object.
Item 20 中提到,多数情况下传引用比传值更好。但不要无脑追求这一点,一定不要返回空引用或指针。
Item 21: Don’t to return a reference when you must return an object.
Item 20 中提到,多数情况下传引用比传值更好。但不要无脑追求这一点,一定不要返回空引用或指针。
Item 20: Prefer pass-by-reference-to-const to pass-by-value.
缺省情况下C++
用传值得方式(一个继承自C
的方式)传递对象至(或来自)函数。除非你另外指定,否则函数参数都是以实际实参的复件(副本)为初值,而调用端所获得的亦是函数返回值的一个复件。这些复件(副本)系由对象的copy
构造函数产出。
尽量以传常量引用替换传值前者通常比较高效,并可避免切割问题 (slicing problem
),但是内置类型和 STL
迭代器,还是传值更加合适。。
Item 19: Teat class design as type design.
特征 | new | malloc |
---|---|---|
类型 | 关键字 | 标准库函数 |
支持语言 | C/C++ |
只在 C++ |
申请内存的位置 | 自由存储区 free store |
堆 |
分配内存的大小 | 编译器根据类型信息自行计算 | 显示指定字节数 |
内存分配失败 | 抛出 bac_alloc 异常, 不会返回 NULL |
返回 NULL |
返回类型 | 返回对象类型的指针 | 返回void 指针,需要转换 |
是否调用构造函数/析构函数 | 调用 | 不调用 |
处理数组 | new[] |
手动指定数组的大小 |
是否支持重载 | 支持 | 不支持 |
是否支持内存扩充 | 不支持 | realloc |
内存释放方式 | new/delete , new[]/delete[] |
malloc/free |
Item 18: Make interfaces easy to use correctly and hard to use incorrectly.
Item 17: Store newed objects in smart pointers in standalone statements.
以单独的语句将 new
的对象放入智能指针内。这是为了防止由于其他表达式抛出异常而导致的资源泄漏。
举个栗子:
1 | processWidget(shared_ptr<Widget>(new Widget), priority()); |
上述代码中,在 processWidget
函数被调用之前参数会首先得到计算。可以认为包括三部分的过程:
new Widget
shared_ptr<Widget>
priority()
因为C++不同于其他语言,函数参数的计算顺序很大程度上决定于编译器,编译器认为顺序应当是1, 3, 2,即:
new Widget
priority()
shared_ptr<Widget>
那么如果 priority
抛出了异常,新的 Widget
便永远地找不回来了。虽然我们使用了智能指针,但资源还是泄漏了!
于是更加健壮的实现中,应当将创建资源和初始化智能指针的语句独立出来:
1 | shared_ptr<Widget> pw = shared_ptr<Widget>(new Widget); |
Item 16: Use the same form in corresponding uses of new and delete.
如果你用 new
申请了动态内存,请用 delete
来销毁;如果你用 new xx[]
申请了动态内存,请用 delete[]
来销毁:
举个栗子:
1 | std::string* stringPtrl = new std::string; |
上面很容易理解但需要注意typedef
:
1 | typedef std::string AddressLines[4]; //每个人的地址有四行, |
由于 AddressLines
是个数组,如果这样使用 new
:
1 | std::string *pal = new AddressLines; //注意. "new AddressLines" 返回 |
那就必须匹配 “数组形式“的 delete
:
1 | delete pal; //行为未有定义! |
为避免诸如此类的错误,最好尽量不要对数组形式做 typedefs
动作。可以使用更加面向对象的vector
、string
等对象。
Item 15: Provide access to raw resources in resource-managing classes.
APIs
往往要求访问原始资源(raw resources
),所以每一个RAII class 应该提供提供对原始资源访问的方法。获取资源的方式有两类:隐式地获取和显式地获取。 显式的资源获取会更安全,它最小化了无意中进行类型转换的机会。
shared_ptr
提供了 get
方法来得到资源。
1 | shared_ptr<Investment> pInv; |
为了让 pInv
表现地更像一个指针,shared_ptr
还重载了解引用运算符(dereferencing operator
) operator->
和 operator*
:
1 | class Investment{ |
我们封装了Font来管理资源:
1 | class Font{ |
通过get方法来访问FontHandle:
1 | Font f(getFont()); |
可以隐式类型转换运算符将 Font
转换为 FontHandle
:
1 | class Font{ |
然而问题也随之出现:
1 | FontHandle h2 = f1; |
无意间 h2
并没有被资源管理起来,这将会引发意外的资源泄漏。所以隐式转换在提供便利的同时, 也引起了资源泄漏的风险。