
Effective C++
CCSUZB
吾生也有涯,而知也无涯
展开
-
条款48:认识template元编程
TMP(模板元编程)可將工作由运行期移往编译器,因而得以实现早期错误侦测和更高的执行效率原创 2022-02-09 15:52:59 · 363 阅读 · 0 评论 -
条款47:请使用traits classes表现类型信息
假定我们有一个名为advance,用来將某个迭代器移动某个距离:template<typename IterT, typename DistT> // 將迭代器移动d个单位void advance(IterT &iter, DistT d);STL迭代器分类有5种:intput迭代器,只能向前移动,一次一步,只可以读取,如istream_iterators,只能读取一次output迭代器,只能向前移动,一次一步,只可以涂写,如otream_iterators,只能涂写一次原创 2022-02-08 18:11:10 · 804 阅读 · 0 评论 -
条款46: 需要类型转换时请为模板定义非成员函数
回顾条款24:唯有non-member函数才有能力在"所有实参身上实施隐士类型转换",当遇到模板时候考虑如下代码:template<typename T>class Rational {public: Rational(const T &numerator = 0, const T &denominator = 1); const T numerator() const; const T denominator() const;};templa原创 2021-01-01 16:45:01 · 188 阅读 · 0 评论 -
条款45: 运用成员函数模板接受所有兼容类型
真实指针做得很好的一件事是,支持隐式转换(implicit conversions),比如如下代码:class Top {};class Middle : public Top {};class Bottom : public Middle {};Top *pt1 = new Middle; // 将Middle转化为Top*Top *pt2 = new Bottom; // 将Bottom转化为Top*const Top *pct2 = pt1; // 将Top*转化为const原创 2020-12-27 21:04:51 · 136 阅读 · 0 评论 -
条款44: 将参数无关的代码剥离templates
假如你想为固定尺寸的正方矩阵编写一个template.该矩阵的性质之一是支持矩阵逆运算:template<typename T,std::size_t n>class SquareMatrix {public: //... void inver(); // 求逆矩阵};这个 template接受一个类型参数T,和一个类型为size_t的非类型参数;然后考虑如下代码调用:SquareMatrix<double 5> sm1;sm1.invert(); // 调用Sq原创 2020-12-20 21:32:30 · 129 阅读 · 0 评论 -
条款43: 学习处理模板化基类内的名称
假设我们需要撰写一个程序,它能够传送信息到若干不同的公司去:class CompanyA {public: // ... void sendClearText(const std::string &msg); void sendEncrypTed(const std::string &msg);};class CompanyB {public: // ... void sendClearText(const std::string &原创 2020-12-14 17:29:34 · 144 阅读 · 0 评论 -
条款42:了解typename的双重意义
template<class T> class Widget; //使用classtemplate<typename T> classWidget; //使用typename当我们声明template类型参数,class和typename的意义完全相同原创 2020-12-13 21:41:14 · 159 阅读 · 0 评论 -
条款41:了解隐式接口和编译期多态
List item原创 2020-12-11 21:37:38 · 190 阅读 · 0 评论 -
条款40: 明智而审慎地使用多重继承
List item原创 2020-12-08 21:53:59 · 169 阅读 · 0 评论 -
条款39:明智而审慎地使用private继承
考虑如下代码:class Person {};class Student : private Person {};void eat(const Person& p); // 任何人都会吃void study(const Student& s); // 只有学生在校学习Person p; // p是人Student s; // s是学生eat(p); // 没问题eat(s); // 错误!难道学生不是人?上述eat(s)错误的原因有两点:如果classes之原创 2020-12-06 21:18:18 · 254 阅读 · 0 评论 -
条款38:通过复合塑模出has-a或“根据某物实现出“
复合(composition)是类型之间的一种关系,考虑如下代码:class Address { }; // 住址class PhoneNumber {};class Person {private: std::string name; Address address; PhoneNumber voiceNumber; PhoneNumber faxNumber;};Person对象是由string,Address,PhoneNumber构成;上述Person class示范has-原创 2020-11-24 21:08:36 · 151 阅读 · 0 评论 -
条款37:绝不重新定义继承而来的缺省参数值
条款36说明了重新定义一个继承而来的non-virtual函数永远是错误的,所以本条款的理由就是virtual函数系动态绑定,而缺省参数值确实静态绑定考虑如下代码:// 一个用于描述几何形状的classclass Shape {public: enum ShapeColor { Red, Green, Blue }; // 所有形状都必须提供一个函数,绘出自己 virtual void draw(ShapeColor color = Red) const = 0;};class Re.原创 2020-11-22 14:24:07 · 147 阅读 · 0 评论 -
条款36:觉不重新定义继承而来的non-virtual函数
考虑如下代码class B {public: void mf(); // ...};class D : public B {//..};D x; // x是一个类型为D的对象//如果有以下行为B* pB = &x; // 获得一个指针指向xpB->mf(); // 经由该指针调用mf//异于以下行为D* pD = &x; // 获得一个指针指向xpD->mf(); // 经由该指针调用mf但是如果mf是一个non-virtual函数而D定义有自己原创 2020-11-22 11:16:12 · 120 阅读 · 0 评论 -
条款34:区分接口继承和实现继承
表面上直接了当的public继承概念,是由两部分组成的:函数接口继承和函数实现继承;考虑一个如下绘图程序代码:class Shape { public: virtual void draw() const = 0; virtual void error(const std::string &msg); int objectID() const; // ...};class Rectangle : public Shape{ //...};class Ellipse .原创 2020-11-15 12:56:57 · 210 阅读 · 0 评论 -
条款33: 避免遮掩继承而来的名称
考虑如下代码:int x; // global变量void someFunc() { double x; // local变量 std::cin >> x; // 读一个新值赋予local变量x}someFunc函数内使用的x不是为全局变量x,因为ie内层作用域的名称会遮掩外围作用域的名称当导入继承情况时候,derived class作用域被嵌套在base class作用域内:class Base {private: int x;public: virtual原创 2020-11-01 20:57:18 · 217 阅读 · 0 评论 -
条款32:确定你的public继承塑模出is-a关系
如果你令class D以public形式继承class B,你便是告诉C++编译器,每一个类型为D的对象同时也是一个类型为B的对象,反之不成立;考虑以下代码:class Person {};class Student : public Person {};void eat(const Person &p); // 任何人都会吃void study(const Student &s) // 只有学生才到校学习Person p; // p是人Studen原创 2020-11-01 20:01:36 · 254 阅读 · 1 评论 -
条款31:将文件间的编译依存关系将至最低
考虑如下代码:class Person {public: Person(const std::string& name, const Date& birthday, const Address& addr); std::string name() const; std::string brithDate() const; std::string address() const; //...private: std::string原创 2020-10-27 21:44:26 · 163 阅读 · 0 评论 -
条款30:透彻了解inlining的里里外外
inlie函数背后整体观念是,将"对此函数的每一个调用"都以函数本体替换之Inlining在大多数C++程序中是编译行为大部分编译器拒绝将太过复杂(例如循环或递归)的函数inlining,而所有对virtual函数的调用也会使inlining落空,因为virtual意味着"等待,知道运行期才确定调用那个函数",而inling意味着"执行前,先将调用动作替换为被调用函数的本体";编译器通常不对"通过函数指针而进行的调用"实施inlining,这意味着对inline函数的调用有可能被inlined,也有.原创 2020-10-20 22:23:18 · 292 阅读 · 0 评论 -
条款29:为“异常安全“而努力是值得的
考虑如下代码:class PrettyMenu { public: // ... void changeBackground(std::istream& imgSrc); // 改变背景图像 // ... private: Mutex mutex; // 互斥器 Image* bgImage; // 目前背景图像 int imageChanges; // 背景图像被改变次数};void PrettyMenu::changeBackground(std原创 2020-10-16 22:27:34 · 115 阅读 · 0 评论 -
条款28:避免返回handles(引用,指针,迭代器)指向对象内部成分
考虑如下代码:class Point {public: Point () {} Point(int x, int y) : mx(x), my(y) {} void setX(int newVal) { mx = newVal; } void setY(int newVal) { my = newVal; } int xpos() { return mx; } int ypos原创 2020-10-09 22:01:54 · 155 阅读 · 0 评论 -
条款26:尽可能延后变量定义的出现时间
考虑如下代码在这里插入代码片原创 2020-10-06 19:48:07 · 229 阅读 · 1 评论 -
条款25:考虑写一个不抛出异常的swap函数
考虑如下代码class WidgetImp1 { //针对Widget数据设计的class,细节不重要public: //...private: int a, b, c; std::vector<double> v; };class Widget {public: Widget(const Widet& rhs); Widget& operator=(const Widget& rhs) { //... *pImpl = *(rhs.pI原创 2020-10-04 14:39:01 · 155 阅读 · 0 评论 -
条款24:若所有参数皆需类型转换,请为此采用non-member函数
考虑如下支持算数运算的类class Rational { public: Rational(int numerator = 0, int denominator = 1); int numerator() const; int denominator() const; //分子,分母函数 const Rational operator* (const Rational& lhs) const; //乘法函数 private: // ...};将两个有理数以最轻松自在的原创 2020-10-03 15:10:20 · 198 阅读 · 0 评论 -
条款23:宁以non-member,non-friend替换member函数
考虑如下代码:有清除缓冲区,历史记录,cookies等操作class WebBrowser { public: //... void clearCache(); void clearHistory(); void removeCookies();};有用户想一次性执行这些操作,有如下两种方案,提供一个clearEverything成员函数调用其他三个成员函数class WebBrowser { public: //... void clearEverything(); // 调原创 2020-10-03 10:09:34 · 157 阅读 · 0 评论 -
条款22:将成员变量声明为private
切记将成员变量声明为private,这可赋予客户访问数据的一致性,可细微划分为访问控制,允许约束条件获得保证,并提供class作者以重复以充分的实现弹性protected并不比public更具封装性原创 2020-10-02 22:15:03 · 138 阅读 · 0 评论 -
条款21:必须返回对象时,别妄想返回其reference
知道了pass -by-value(传值)的效率牵连层面,从此一直适用pass-by-reference,就会犯下一个致命错误:开始传递一些reference指向其实并不存在的对象;考虑如下代码:class Rational { public: Rational(int numberator = 0, int denominator = 1); //... private: int n, d; friend const Rational operator* (const Rationa原创 2020-10-02 11:17:27 · 158 阅读 · 0 评论 -
条款20:宁以pass-by-reference-const替换pass-by-value
List item原创 2020-10-01 22:34:41 · 170 阅读 · 0 评论 -
条款18:让接口容易被正确使用,不易被勿用
假设你为一个用来表现日期的class设计构造函数:class Date { public: Date(int month, int day, int year);};以上用户容易犯下如下错误,1:一错误的次序传递参数Date d(30, 3, 1995);2:传递一个无效的月份或天数:Date d(2, 30, 1995);一个解决办法是导入见得外覆类型来区别天数,月份,年份,然后与Date构造函数中使用这些类型:struct Day { explicit Day(int原创 2020-09-26 16:47:43 · 88 阅读 · 0 评论 -
条款17:以独立语句将newed对象置入智能指针
考虑下面两个函数int priority();void processWidget(std::shared_ptr<Widget> pw, int priority);然后考虑调用processWidgetprocessWidget(new Widget, priority());但是无法通过编译,因为无法将new Widget的原始指针转化为processWidget所要求的只能指针,解决办法是将调用改写为如下形式:processWidget(std::shared_ptr&l原创 2020-09-21 22:17:55 · 148 阅读 · 0 评论 -
条款16:成对使用new和delete时要采取相同的形式
如果你在new 表达式中使用[],必须在相应的delete表达式中也使用[];如果你在new表达式中不使用[],一定不要在相应的delete表达式中使用[]原创 2020-09-20 22:12:11 · 109 阅读 · 0 评论 -
条款15在资源管理类中提供对原始资源的访问
从条款13创建一个智能指针:std::shared_ptr<Investment> pInv(creatInvestment());假设你希望某个函数处理Investment对象:int daysHeld(const Investment* pi) 然后想要这么调用int days = daysHeld(pInv),但是通不过编译,*因为daysHel需要的是Investment 指针,但是传过去的却是一个类型为std::shared_ptr<Investment> 的对原创 2020-09-20 08:47:29 · 124 阅读 · 0 评论 -
条款14:在资源管理类中小心copying行为
假设我们使用C API函数处理类型为Mutex的互斥器对象,共有lock和unlock两函数可用:void lock(Mutex* pm) //锁定pm所指的互斥器void unlock(Mutex* pm) //将互斥器解除锁定为了确保不会忘记将一个被锁住的Mutex解锁,可能会希望建立一个class用来管理机锁class Lock { public: explicit Lock(Mutex* pm) : mutextPtr(pm) { lock(mutextPtr); }原创 2020-09-19 18:36:54 · 171 阅读 · 0 评论 -
条款13:以对象管理资源
建立一个Investment类,一个工场函数class Investment{} ;Inversment* createInvestment(); // 返回指针,指向Investment继承体系内的动态分配对象有个函数f调用了createInvestmentvoid f() { Investment* pInv = createInvestment(); ... delete pInv;},上述看起来释放了资源pInv,但是如果"…"区域内一个return语句或者其他抛出异常都将会导原创 2020-09-19 16:59:07 · 115 阅读 · 0 评论 -
条款12:复制对象时勿忘其每一个成分
class Date {public: std::string date;};class Customer {public: Customer(const Customer& rhs); Customer& operator =(const Customer& rhs);private: std::string name; Date lastTransaction;};Customer::Customer(const Cust原创 2020-07-05 22:11:55 · 115 阅读 · 0 评论 -
条款10:令operator=返回一个reference to *this
实现连锁赋值class A{public: int x; void operator=(const A& rhs) { x = rhs.x; }};A a, b, c;a = b = c;/* 如果operator= 的返回值不为reference to *this则上面无法通过编译 这是个协议,无强制性,除非你有更好的理由,否则原创 2017-09-23 11:05:05 · 246 阅读 · 0 评论 -
条款11:在operator=中处理自我赋值
别在停止使用资源前释放它class Bitmap{};class Widget{public: Widget() { pb = NULL; } Bitmap *pb ; Widget& operator=(const Widget&);};Widget& Widget::operator=(const Widget& rhs){原创 2017-09-23 19:57:22 · 279 阅读 · 0 评论 -
条款03:尽可能使用const
顶层const与底层const①如果关键字出现在星号左边表明被指物是常量;如果星号出现在右边,表明指针自身是常量;如果出现在星号两边,表明被指物和指针都是常量 int i = 0, j = 3; float k= 1; int* const p = &i; //表明指针p是一个常量不可以改变 p = &j; //错误 const int *p1原创 2017-09-15 21:30:34 · 244 阅读 · 0 评论 -
条款04:确定对象被试用前已被初始化
赋值和初始化使用构造函数初始化比赋值效率高,成员的初始化顺序与它们在类中定义中出现的顺序一致class PhoneNumber{};class ABEntry{public: ABEntry(const string& name, const string& address, const list<PhoneNumber>& phones);private: string原创 2017-09-17 10:45:26 · 232 阅读 · 0 评论 -
条款05:了解C++默认编写调用哪些函数
编译器可以暗自为class创建default构造函数,copy构造函数,copy assignment操作符,以及析构函数。class Empty{};//一个空的Empty类等价于下面class Empty{public: Empty() {}; //default构造函数 Empty(const Empty& rsh) {}; //copy构造函数 ~Em原创 2017-09-18 18:50:00 · 568 阅读 · 0 评论 -
条款06:若不想使用编译器自动生成的函数,就该明确拒绝
把copy构造函数或copy assignment操作符声明为privateclass HomeForSale{public: HomeForSale() { } private: HomeForSale(const HomeForSale&); HomeForSale& operator=(const HomeForSale&);};HomeFo原创 2017-09-18 19:50:37 · 402 阅读 · 0 评论