每个人都有思想.有些人相信自由经济学,有些人相信来生.有些人甚至相信COBOL是一种真正的程序设计语言.C++也有一种思想:它认为潜在的二义性不是一种错误.ambiguity 这是潜在二义性的一个例子: class B; // 对类B提前声明 // class A {public: A(const B&); // 可以从B构造而来的类A}; class B {public: operator A() const; // 可以从A转换而来的类B}; 这些类的声明没一点错——他们可以在相同的程序中共…
尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率.…
因为构造和析构函数有开销,所以也许前面定义了,还没用函数就退出了. 所以比较好的方法是用到了才定义.…
尽可能延后变量定义式的出现时间 我们知道定义一个对象的时候有一个不争的事实,那就是分配内存.假设是我们自己定义的对象.程序运行过程中会调用类的构造函数和析构函数. 我们打个例如,假设天下雨了,你带把雨伞肯定是值得的. 但是,假设你带伞了,今天却没下雨,你是不是感觉自己亏了?的确,亏在了带了却没用,所以伞就变成了累赘. 本节的关键就在于此,假设你定义一个变量或者对象没有被使用,那么就是不完美的代码. 我们看一个代码片段: std::string encryptPassword(const std:…
条款26 尽可能延后变量定义式的出现时间(Lazy evaluation) 记住: ★尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率 ---------------------------------------------------------------------- 举例说明: std::string encryptPassword( const std::string &password ) { using namespace std; string encrypt…
(这里的验证结果是针对返回值优化的,其实和条款22本身所说的,考虑以操作符复合形式(op=)取代其独身形式(op),关系不大.书生注) 在[More Effective C++]条款22的最后,在返回值的返回方式上,大师Meyers推荐使用表达式[returnT(lhs)+=rhs;]这种使用匿名临时变量的方式,理由是“自古以来未具名对象总是比具名对象更容易被消除”,这种写法将更好地帮助编译器实现返回值优化(ReturnValue Optimization,简写RVO). 针对上述说法,我在两款…
More Effective C++ 条款0,1 条款0 关于编译器 不同的编译器支持C++的特性能力不同.有些编译器不支持bool类型,此时可用 enum bool{false, true};枚举类型来模拟bool类型.这允许参数类型为int和bool的函数重载,但是这样做的缺陷是,对于内置的比较运算符,其仍返回int类型. f(int);f(bool); f(a < b); // 会调用f(int),但其实用户期望调用f(bool). 但是一旦改用支持bool类型的编译器,情况可能会发生改变…
问题: 如何限制类对象的个数?比如1个,10个等等. 方法(1): 将类的构造函数定义为private,那么就无法实例化这个类了.但是如何创建1个对象出来?方法有2种: 1.声明一个友元函数,那么在友元函数中就可以调用构造函数了,创建对象时使用static限制,那么就保证只有一个对象了.类似的定义如下: class Printer { public: friend Printer& thePrinter(); private: Printer(); Printer(const Printer&am…
(一) 那么当程序的控制流到达这个变量定义时.变承受构造成本:当变量离开作用域时.便承受析构成本. string encryptPassword(const std::string& password) { using namespace std; string encrypted; if(password.length() < MinimumPasswordLengt) { throw logic_error("Password is too short") } -//…
1.别让异常逃离析构函数的原因 <Effective C++>第三版中条款08建议不要在析构函数中抛出异常,原因是C++异常机制不能同时处理两个或两个以上的异常.多个异常同时存在的情况下,程序若不结束,会导致不明确行为.如下代码: class Widget{ public: ~Widget(){...} //假设这个可能吐出一个异常 }; void dosomething(){ vector<Widget> v; } //v在这里被自动销毁 函数dosomething运行结束后,最…
避免返回handles(包括reference.指针.迭代器)指向对象内部.遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”(dangling handles)的可能性降至最低.…
绝不要返回pointer或reference指向一个local stack对象,或返回reference指向一个heap-allocated对象,或返回pointer或reference指向一个local static对象而有可能同时需要多个这样的对象.条款4已经为“在单线程环境中合理返回reference指向一个local static对象”提供了一份设计实例.…
Class的设计就是type的设计.在定义一个新type之前,请确定你已经考虑过本条款覆盖的所有讨论主题. 新type的对象应该如何被创建和销毁? 对象的初始化和对象的赋值该有什么样的区别? 新type的对象如果被passed by value(以值传递),意味着什么? 什么是新type的“合法值”? 你的新type需要配合某个继承图系(inheritance graph)吗? 你的新type需要什么样的转换? 什么样的操作符和函数对此新type而言是合理的? 什么样的标准函数应该驳回? 谁该取…
好的接口很容易被正确使用,不容易被误用.你应该在你IDE所有接口中努力达成这些性质. “促进正确使用”的办法包括接口的一致性,以及与内置类型的行为兼容. “阻止误用"的办法包括建立新类型.限制类型上的操作,束缚对象值,以及消除客户的资源管理责任. tri::shared_ptr支持定制型删除器(custom deleter).这可防范DLL问题,可被用来自动解除互斥锁(mutexes;见条款14)等等.…
条款27:尽量少做转型动作 单一对象可能拥有一个以上的地址!…
本节条款的题目是运用成员模板接受全部兼容类型 作者阐述自己的观点是通过智能指针的样例. 在学习本节条款之前我们要先明确关于隐式转化的问题 例如以下代码: #include<iostream> using namespace std; class A { public: explicit A(int i):a(i){}; A(const A&obj):a(obj.a) { } private: int a; }; int main() { int value =0; A a = valu…
我从本条款中学到了下面内容: 1.private继承不同于另外两种继承,派生类对象不能隐式转换为基类对象. 例如以下代码: class Bird//鸟 { }; class ostrich:private Bird//鸵鸟 { }; int main() { Bird *b = new ostrich();//编译不通过,基类不能转换为派生类 } 编译器明白指出基类是不可訪问的.所以转换失败. private继承在基类和派生类之间已经没有所谓的继承关系,之所以有它的存在纯粹是为应用层面服务,即为…
这个条款应该和第五个放在一起 编译器默认生成 1 无参构造函数, 2 析构函数 3 拷贝构造函数 4 赋值预算符 当我们不需要,就要显示的告诉大家我们不需要. 方法:自己声明这个函数为private,单不要定义它. 原因:1 自己声明这个函数,编译器就不会为我们生成这个函数 2 声明成private,就不会被对象误调用,但是freind和成员函数还是可以调用 3 声明但是不定义,当其他成员函数或者freind企图调用时就会发生编译错误…
本节条款的标题是:将与參数无关的代码抽离templates 学习本节条款首先须要明确一件事情,那就是模板实例化的过程会不会反复? 我们来举个样例: #include<iostream> using namespace std; template <typename T> T Try(T m) { return m; } int main() { Try(10); Try(1); } 我们看以上简单的代码.每一个人都知道模板被调用了两次,是的,这显而易见.但是模板也被实例化了两次吗?…
1. 定义变量会引发构造和析构开销 每当你定义一种类型的变量时:当控制流到达变量的定义点时,你引入了调用构造函数的开销,当离开变量的作用域之后,你引入了调用析构函数的开销.对未使用到的变量同样会产生开销,因此对这种定义要尽可能的避免. 2. 普通函数中的变量定义推迟 2.1 变量有可能不会被使用到的例子 你可能会想你永远不会定义未使用的变量,你可能要再考虑考虑.看下面的函数,此函数返回password的加密版本,提供的password需要足够长.如果password太短,函数会抛出一个logic…
本节条款:须要类型转换时请为模板定义非成员函数 这节知识是在条款24的基础上,讲述的有关非成员函数在模板类中(non-member function template)的作用. 我们先看一下条款24讲述的知识核心.条款24讲述了我们怎样能实现类的对象在特定条件下的隐式转换问题. 我们先看以下代码: ** 例一: ** #include<iostream> #include<assert.h> using namespace std; class Rational { private…
尽量少做转型动作 尽量少做转型动作有什么目的?非常明显无非就是提高程序的稳定性.提高程序的运行效率. 那么.有哪些转型方式?每种方式都有什么弱点? 这是我们本节学习的重点. C++有四种转型: const_cast<T>(expression) dynamic_cast<T>(expression) reinterpret_cast<T>(expression) static_cast<T>(expression) 每种转型的作用例如以下: 1.const_…
(一) 为什么有人想要替换operator new 和 operator delete呢?三个常见的理由: (1)用来检測运用上的错误. (2)为了强化效果. (3)为了收集使用上的统计数据. (二) 以下是个高速发展得出的初阶段global operator new.促进并协助检測"overruns"或"underruns". static const int signature = 0xDEADBEEF; typedef unsigned char Byte;…
"自我赋值"发生在对象被赋值给自己时: class Widget { ... }; Widget w; ... w = w; // 赋值给自己 a[i] = a[j]; // 潜在的自我赋值 *px = *py; // 潜在的自我赋值 class Base { ... }; class Derived: public Base { ... }; void doSomething(const Base& rb, Derived* pd); // rb和*pd有可能其实是同一对象…
本节条款的题目:请使用trait classes来表示类型信息 本节条款主要讲述的技术是怎样在编译期间实现对迭代器类型的推断,依据推断的类型进行最优处理. 我们先来看一下迭代器的种类: 1.input_iterator:仅仅读,仅仅能逐个前移 2.output_iterator:仅仅写,仅仅能逐个前移 3.forward_iterator:可读可写,仅仅能逐个前移 4.bidirectional_iterator:可读可写,支持逐个前移和后移 5.random_access_iterator:可…
1.在资源管理类中提供对原始资源的访问     前几个条款很棒,它们是对抗资源泄露的壁垒,但很多APIs直接指向 资源,这个时候,我们需要直接访问原始资源.     这里,有两种方法解决上述问题,我们可将RAII对象转换为原始资源.通过 显式转换与隐式转换.     通常,tr1:: shared_ptr 和 auto_ptr 都提供一个get成员函数,用来执行显式转换,也就是返回智能指针内部的原始指针的复件.因为它也重载了指针取值操作符* –>.当然也可以通过隐式转换为底部原始指针.     …
1.潜在的自我赋值     a[i] = a[j];     *px = *py; 当两个对象来自同一个继承体系时,他们甚至不需要声明为相同类型就可能造成别名. 现在担心的问题是:假如指向同一个对象,当其中一个对象被删,另一个也被删,这会造成不想要的结果. 该怎么办? 比如:   widget& widget:: operator+ (const widget& rhs) {    delete pd;    pd = new bitmap(*rhs.pb);    return *thi…
/************************************************************************/ /* C++条款二:用编译器替换预编译器 */ /************************************************************************/   #include <iostream> using namespace std;   class GamePlayer { private: st…
本节条款我们讨论一下class 关键字和typename关键字的不同以及对于模板函数(template function)的影响. 例如以下代码: template<class T> T getValue1(T m) { return m * 2; } template<typename T> T getValue2(T m) { return m * 2; } 在上面代码中.class和typename的作用是一样的.没有不论什么区别.所以程序猿在声明模板时能够依据个人习惯使用c…
复制对象时,勿忘其每个成分 作者在本节条款提醒我们,在多重继承的情况下进行copy或者copy assignment 的operator=的编写时,一定要考虑base 类部分数据的初始化后者复制. 对照一下代码: class Cutsomer { -- private: string name; string telphone; }; class PriorityCustomer:public Cutsomer { public: PriorityCustomer() { cout<<&quo…