《effective c++》读书笔记2

2024-06-13 01:58
文章标签 c++ 读书笔记 effective

本文主要是介绍《effective c++》读书笔记2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

条款13:以对象管理资源

使用RAII,获得资源后立刻放进管理对象,管理对象运用析构函数确保资源被释放。

为防止资源泄露,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源。

两个常被使用的RAII类是tr1::shared_ptr和tr1::auto_ptr。前者通常是较佳选择,因为其拷贝行为比较直观。若选择auto_ptr,复制动作会使被复制物指向null.

 条款14:在资源管理类中小心拷贝行为

n  复制RAII对象必须一并复制它所管理的资源,所以资源拷贝行为决定RAII对象的拷贝行为。

n  普遍而常见的RAII类拷贝行为是:抑制拷贝,使用引用计数法。

条款15:在资源管理类中提供对原始资源的访问

n  APIs往往要求访问原始资源,所以每一个RAII类应该提供一个取得其所管理之资源的办法

n  对原始资源的访问可能经由显示转换或隐式转换。一般而言显式转换比较安全,但隐式转换对客户比较方便。

条款16:成对使用new和delete时要采取相同形式

考虑下面的typedef,:

typedef std::string AddressLines[4];

由于AddressLines是个数组,如果这样使用new;

std::string *pal=new AddressLInes;

那就必须匹配数组形式的delete:

delete pal;   //未定义行为

delete []pal;  //正确

n  如果你在new表达式中使用[],必须在相应的delete表达式中也使用[]。如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]。

条款17:以独立语句将newed对象置入智能指针

考虑以下代码:

int priority(); //处理程序的优先级

void processWidget(std::tr1::shared_ptr<Widget>  pw,int priority);

processWidget(std::tr1::shared_ptr<Widget>(new Widget),priority());

以上代码可以通过编译,但却不是异常安全的,因为new Widget和priority()的调用顺序是不确定的。如果调用的顺序是这样的:

1.      执行new Widget

2.      调用priority

3.      调用tr1::shared_ptr构造函数

如果调用priority时出现了异常,new Widget申请的资源将会出现内存泄露。

避免上述问题的方法是分离语句:

std::tr1:shared_ptr<Widget> pw(newWidget);

processWidget(pw,priority());

这样,编译器就不能任意选择语句的执行顺序了。

n  以独立语句将newed的对象存储到智能指针内。如果不这样做,一旦异常被抛出,有可能导致难以察觉的资源泄露。

 条款18:让接口容易被正确使用,不易被误用

n  好的接口容易被正确使用,不容易被误用,你应该在你的所有接口中努力达成这些性质。

n  促进正确使用的办法包括接口的一致性,以及与内置类型的行为兼容。

n  阻止误用的办法包括建立新类型,限制类型上的操作,束缚对象值,以及消除客户的资源管理责任。

条款19:设计class犹如设计类型

设计优秀的calsses是一项艰巨的工作,因为设计好的types是一项艰巨的工作。好的types有自然的语法,直观的语义,以及一个或者多个高效实现品。在C++中,一个不良规划下的class定义恐怕无法达到上述任何一个目标,甚至class的成员函数的效率有可能受到他们如何被声明的影响。

条款20:宁以传递指向const的引用代替传值

考虑以下类:

classWindow

{

public:

       std::string name() const;

       virtualvoid display()const;

};

 classWindowWithScrollBars:public Window

{

public:

       virtualvoid display()const;

};

 

voidprintNameAndDisplay(Window w)

{

std::cout<<w.name()<<endl;

w.display();

}

如果调用上述函数并交给它一个Window对象,在函数内调用的是Window::display,而不会调用派生类的display函数,解决上述问题的办法是:

void printNameAndDisplay(const Window&w)

{

std::cout<<w.name();

w.display();

}

 传引用的方式相比于传值的方式,有两个优点,一是提高程序的执行效率二是避免了对象的分割问题。

 n  尽量以传递指向常量的引用代替传值,前者通常更加高效,并可避免切割问题

n  以上规则并不适用于内置类型,以及STL的迭代器和函数对象。对它们而言,传值往往比较适当。

条款21:必须返回对象时,别妄想返回其引用

如果重载乘法运算符如下:

const Rational & operator *(constRational &lhs,const Rational &rhs)

{

Rational result(lhs.n*rhs.n,lhs.d*rhs.d);

return result;

}

 这个函数返回了局部变量的引用,因而是错误的。

在看另外一个版本:

const Rational & operator *(constRational& lhs,const Rational & rhs)

{

Rational *result=newRational(lhs.n*rhs.n,lhs.d*rhs.d);

return *result;

}

这个函数完成了对象的初始化工作,但是堆上分配的内存无法释放因为会造成内存泄露问题。

必须返回一个对象时,函数的正确写法是:就让那个函数返回一个新对象。

函数可以改写如下:

inline const Rational operator *(constRational &lhs,const Rational &rhs)

{

return Rational(lhs.n*rhs.n,lhs.d*rhs.d);

}

 为了获取正确的结果,必须忍受一个构造函数和析构函数的代价。

 条款22:将成员变量声明为private

n  切记将成员变量声明为private,这可赋予客户访问数据的一致性,可细微划分访问控制,允诺约束条件获得保证,并提供class作者以充分的实现弹性。

条款23:宁以non-member non-friend函数替换member函数

考虑以下类:

classWebBrower

{

public:

       voidclearCache();

       voidclearHistory();

       voidremoveCookies();

};

 如果想要连续调用三个函数,可以这样设计一个成员函数:

classWebBrowser

{

public:

       voidclearEverything();  //调用上面三个函数

};

 这个函数也可以使用以下的版本代替:

voidclearBrowser(WebBrowser & wb)

{

wb.clearCache();

wb.clearHisttory();

wb.removeCookies();

}

 这个非成员非友元的版本要比成员函数的版本更好,因为它不能访问private成员,提供了更大的封装性。

 条款24:若所有参数皆需类型转换,请为此采用非成员函数

考虑以下类的定义:

class Rational

{

         public:

                   constRational operator *(const Rational *rhs) const;

                   …

}

 尝试如下方式调用:

result=oneHalf*2; //正确

result=2*oneHalf;  //错误

只有参数被列于参数列表中时,这个参数才是隐式类型转换的合格参与者。

 n  如果想让result=2*oneHalf;也成为合法的调用,可以把operator*()函数写成非成员函数的形式。

如果需要为某个函数的所有参数进行类型转换,那么这个函数必须是个非成员函数。

条款25:考虑写出一个不抛出异常的swap函数

n  当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常

n  如果你提供一个memberswap,也该提供一个non-member swap用来调用前者,对于类也请特化std::swap


这篇关于《effective c++》读书笔记2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

2024/9/8 c++ smart

1.通过自己编写的class来实现unique_ptr指针的功能 #include <iostream> using namespace std; template<class T> class unique_ptr { public:         //无参构造函数         unique_ptr();         //有参构造函数         unique_ptr(