Effective C++ 第一章——让自己习惯C++
Effective C++ 条款1——让自己习惯C++
条款1——视C++为一个语言联邦
C++不再是以前的C with class,而加入了很多新的特性,如Exception(异常)、template(模板)、STL。C++目前包含多重范型编程语言,支持过程形式(procedural)、面向对象形式(object-oriented)、函数形式(functional)、泛型形式(generic)、 元编程形式(metaprogramming)**等。
C++可以看成四个次语言组成:
- C:C++以C为基础,;
- Object-Oriented C++: C语言加上了class、封装、继承、多态、虚函数;
- Template C++: 泛型编程的部分;
- STL:一个template库,容器、迭代器、算法和函数对象;
C++是一个由四个次语言组成的联邦政府、每个次语言都有自己的规约。
请记住:C++高效编程守则视状况而变化,取决于你使用C++的那一部分。
条款2——尽量以const,enum,inline替换#define
1.用const提换#define
使用#define 宏替换时可能不被编译器处理,#define不被认为是语言的一部分;#define不具有封装性。
将
#define ASPECT_RATIO 1.653这种宏定义用常量
const double Aspectratio = 1.653;
以const常量替换#define时,两个特殊情况:
1)定义常量指针:要在头文件中定义一个常量的char*-based字符串,必须写const两次
const char* const authorName = "Scott Meyers";
2)class专属常量:为将常量的作用域限制在类内,需要将其作为一个类的成员,而且要确保此常量至多只有一个实体,需要将其成为一个static成员;
class GamePlayer { private: static const int NumTurns = 5; int scores[NumTurns]; };
2.enum代替#define
当编译器不支持在“in class初值设定”,可以采用“the enum hack”补偿的方法 ;enum绝对不会导致非必要的内存分配;
class GamePlayer { private: enum { NumTurns= 5 }; //“the enum hack” --令NumTurns int scores[NumTurns]; //成为5的一个记号 };
3.inline代替#define
#define常常被用来实现宏(macros),宏可以提高效率,存在不可预料的事情以及宏中所有实参加上括号的麻烦。
如下例:
//以a和b中较大的数调用函数f #define CALL_WITH_MAX(a,b) f((a)>(b)?(a):(b))//必须为宏中所有实参加上括号 int a =5,b=0; CALL_WITH_MAX(++a,b);//a被累加两次 CALL_WITH_MAX(++a,b+10);//a被累加一次
用inline来提升效率可以替代#define定义的宏;
template<typename T> inline void callWithMax(const T& a,const T& b) { f( a > b ? a : b ); }
请记住:
- 对于单纯常量,最好以const对象或enum量替换#define;
- 对于形似函数的宏(macros),最好改用inline函数替换#define;
条款3——尽可能使用const
const:指定一个约束语义,指定一个不允许被改动的对象**;用于修饰global或namespace作用域中的常量、修饰文件、函数、或区块作用域中被声明为static对象,修饰class内部的static和non-static成员变量,还可以修饰指针本身、指针所指物,或者两者都是(都不是)const。
1.const修饰指针:
char greeting[]="holle"; char *p = greeting; //non-const指针,non-const数据 const char *p = greeting;//non-const指针,const数据 char* const p = greeting;//const指针,non-const数据 const char* const p = greeting;//const指针,const数据
const出现在左边,表示所指物为常量;const出现在右边,表示指针本身为常量;const出现在*两边,表示所指物和指针本身都为常量。
2.const修饰函数:可以修饰返回值、各参数、成员函数本身;
- 令函数返回一个常量值,可以降低用户错误而造成的意外,又不至于放弃安全性和高效性。
class Rational {...}; const Rational operator* (const Rational& lhs, const Rational& rhs); //如果这种情况下不返回const量的话,可能有的程序员写出这样的语句 Rational a, b, c; ( a * b ) = c;//错误的
-
const参数:const修饰参数时函数内将不能改变参数;
-
const成员函数:修饰成员函数使成员函数不改变调用的对象;使“操作const对象成为可能”;提升c++程序效率的根本办法就是按照pass by reference-to-const方式传递;
有时候存在类中存在指针时,当我们不改变指针,但是改变指针所指向的值时,编译器认为他是bitwise constness;
mutable可以改变non-static成员变量的bitwise constness约束,这样可以在const成员函数中修改 mutable变量修饰的成员变量; -
在const和non-const成员函数中避免重复:用const成员函数可以实现其non-const孪生函数,需要用到const_cast类型转换;
请记住:
- 将某些函数声明为const,可以帮助编译器侦测出错误用法。const可以被用于任何作用域内的对象、函数参数、函数返回类型、成员函数本体;
- 编译器强制实施bitwise constness,但你编写程序时应该使用概念上的常量性;
- 当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复;
条款4——确定对象被使用前已先被初始化
1 对象必须初始化
读取为初始化的值会导致不明确的行为。
最佳的处理办法是:在使用对象之前将它初始化。
- 对于内置类型,手动完成初始化;
int x = 0;//对int手工初始化 const char * text = "A C-style string";//对指针手工初始化 double d; std::cin>>d;//以 input stream 方式初始化double值
- 对内置类型以外的任何其他东西,确保构造函数将对象的每一个成员初始化;
c++中对象的成员变量的初始化动作发生在进入构造函数体本体之前,最好的办法是使用成员初始化列表member initialization list。
#include<string> #include<list> class PhoneNumber {...}; class ABEntry{ //ABEntry = "Address Book Entry" public: ABEntry(const std::string& name,const std::string& address,const std::list<PhoneNumber>& phones); private: std::string theName; std::string theAddress; std::list<PhoneNumber> thePhones; int numTimesConsulted; }; ABEntry::ABEntry(const std::string& name,const std::string& address, const std::list<PhoneNumber>& phones) { theName = name; //赋值语句 theAddress = address; thePhones = phones; numTimesConsulted = 0; }
上述程序中的构造函数中函数体中的语句
theName = name;...numTimesConsulted = 0;都不是被初始化,而是被赋值。
ABEntry::ABEntry(const std::string& name,const std::string& address, const std::list<PhoneNumber>& phones) :theName(name),theAddress(address),thePhones(phones),numTimesConsulted(0) {}
规定:总是在成员初始化列表中列出所有成员变量,以免还得记住哪些成员变量可以无需初值。
成员变量是const或references,一定要使用初值,而不能被赋值。因此为了避免要记住成员变量何时必须在成员楚淑慧列表中初始化,何时不需要,最简单的办法就是:总是使用成员初始化列表初始化。
2 多个构造函数的初始化处理避免重复代码
许多class拥有多个构造函数,每个构造函数都有自己的成员初始化列表,多个初始化列会导致重复代码。这时可以将一些“赋值与初始化表现一样好”成员变量,改为赋值操作,并将这些赋值操作移往某个函数(通常为private),然后在各个构造函数中调用他。
3 成员初始化次序
按照class中声明的次序初始化,即使在成员初始化列表中出现的顺序不同,最好成员初始化表中列出的顺序与声明次序一致,基类(based classes)先与派生类(derived classes)初始化。
4.不同编译单元内定义的non-local static 对象的初始化次序
不同编译单元内定义的non-local static 对象的解释:
- non-local static 对象:其寿命从被构造出来到程序结束为止,程序结束自动销毁;
- 编译单元:产出单一目标文件的那些源码,基本上是单一源码文件加上其所含有的头文件(#include files);
以函数调用(返回一个reference指向local static对象)替换直接访问“non-local static 对象”。
请记住:
- 为内置类型手动初始化,因为C++不保证初始化他们;
- 构造函数最好使用成员初始化列表,而不是在构造函数中使用赋值操作;初始化列表列出的成员变量其排列次序应该和它们本身在class中声明的次序相同;
- 为免除“跨编译单元的初始化次序”问题,请以local static 对象替换non-local static 对象。
- 《Effective C++》第一章:让自己习惯C++
- Effective c++ 第一章 让自己习惯C++
- Effective c++ 第一章 让自己习惯C++
- Effective C++第一章:让自己习惯C++
- effective c++ 第一章:让自己习惯c++
- Effective C++第一章 让自己习惯C++
- Effective C++ ——让自己习惯C++
- effective C++ 第一章: 让自己习惯C++
- Effective C++之1 让自己习惯C++
- 【Effective C++】条款01-让自己习惯c++
- Effective C++(一)让自己习惯C++
- Effective C++ 笔记一 让自己习惯C++
- Effective C++ 1.让自己习惯c++
- Effective C++ 笔记一 让自己习惯C++
- Effective C++(一)让自己习惯C++
- 读书笔记 Effective C++: 01 让自己习惯C++
- 《Effective C++》1-让自己习惯C++
- 《Effective C++ 3/e》笔记(一):让自己习惯 C++
- 《Effective C++ 3》01 让自己习惯C++ 条款:01-04
- 【读书笔记】Effective C++-1 让自己习惯C++(之三)