现在的位置: 首页 > 综合 > 正文

Effective C++(一)让自己习惯C++

2013年05月23日 ⁄ 综合 ⁄ 共 2974字 ⁄ 字号 评论关闭
一、让自己习惯C++

条款01:视C++为一个语言联邦
1、主要次语言
1)C
有区块、语句、预处理器、内置数据类型、数组、指针
没有模板、异常、重载
2)面向对象C++
类(构造、析构)、封装、继承、多态、虚函数(动态绑定)
3)模板C++
4)STL
模板程序库,容器、迭代器、算法和函数对象
2、次语言之间关系
1)对于内置类型,传值比传址更有效,而由于用户自定义类型构造函数和析构函数的存在,传址更好,特别是模板
2)迭代器和函数对象都是在C指针之上塑造出来的
3、请记住:
C++高效编程守则视情况而变化,取决于你使用C++的哪一部分
条款02:尽量以const,enum,inline
1、#define ASPECT_PATIO 1.653
它被预处理器处理,编译时不会被写入记号表,所以出错只会提到1.653,很难差错!
解决之道:使用常量替换宏(#define)
const double AspectRadio=1.653;
首先编译器处理,所以会写到记号表;同时代码量也较小,共用存在静态存储区,而不像#define替换所有
2、const替换#define的两种特殊情况
1)定义常量指针
定义在头文件,以便被不同源码引用,有必要将指针声明为const
const char * const authorName="Sun Jian";
注:string比char *更适合
const std:string authorName("Sun Jian");
2)class专属常量
常量作用域限制于class内,且至多只有一份实体
static const int NumTurns;   //静态成员
必须另外提供定义
const int GamePlayer::NumTurns=5;
注:#define没有作用域,除非某处被#undef,没有封装性
3、枚举类型
类内部:
enum { NumTurns=5 };
1)枚举类型类似#define,不能取它的地址,但是const可以
2)模板元编程的基础
4、#define实现宏
优点:没有函数调用的内存开销
缺点:注意括号,特别是自增运算被多次计算
5、内联函数
既有宏的效率,同时有函数的所有可预料行为和类型安全性
template<typename T>
inline void callWithMax(const T& a, const T& b)    //T的类型不确定,所以用引用
{
    f(a>b?a:b);
}
6、请记住
1)对于单纯常量,最好以const对象或enums替换#define
2)对于形似函数的宏,最好改用inline函数替换#define
条款03:尽可能使用const
1、const语义约束
某值保持不变
对于指针
const char * p;   //所指对象是常量
char * cosnt p;   //指针自身是常量
const char * const p;   //都是常量
2、STL迭代器
std::vector<int> vec;
const std::vector<int>::iterator iter=vec.begin();    //类似T* const
*iter=10;       //正确,改变iter所指物
++iter;          //错误,iter是const,不能指向其它
std::vector<int>::const_iterator cIter=vec.begin();    //类似const T*
*cIter=10;    //错误,*cIter是const,所指东西的值不能改变
++cIter;        //正确,可以指向不同的东西
3、函数返回常量
防止赋值错误
const参数,不可以改动参数
4、const成员函数
1)确认该成员函数可作用于const对象
①是class接口更容易理解,知道哪个函数可以改动对象内容而哪个函数不可以
②使操作cosnt对象成为可能,特别是传引用
2)例子:两个成员函数如果只是常量性不同,可以被重载
const char& operator[](std::size_t position) const    //可读不可写,不能对返回的const char&赋值
{ return text[position] };
char& operator[](std::size_t position)         //可读可写
{ return text[position] };
textBook tb("Hello");
tb[0]='x';     //返回类型为引用,否则错误!
3)const成员函数不能改变对象中的成员变量(禁止对成员变量的赋值操作),但可以声明为mutable释放约束
const CTextBlock cctb("Hello");    //声明一个常量对象
char *pc=&cctb[0];        //获取一个指针指向cctb的数据
*pc='J';    //正确
5、const和non_const成员函数中避免重复
non-const函数调用const函数
return const_cast<char&>(           //将op[]返回值的const转除
          static_cast<const TextBlock&>(*this)      //为*this加上const,否则递归调用自己
          [position]         //调用const op[]
     );
6、请记住
1)声明为const可帮助编译器侦测出错误用法
2)当const和non-const成员函数有着实质等价的实现时,可以令non-const调用const避免重复
条款04:确定对象被使用前已先被初始化
1、永远在使用对象之前初始化
1)内置类型:手工显示初始化
2)其它:构造函数
2、构造函数初始化
注意赋值和初始化的区别!初始化发生在构造函数本体之前,函数体内是赋值操作!最早的是default构造函数
较佳的写法:初始化成员列表(参数列表和花括号之间)  :theName(name),theAddress(address)
效率更高:比起先调用default构造函数再调用copy assignment操作符,单只调用一次copy构造函数更高效
注:对于内置类型,初始化和赋值的成本相同
规则:总是使用初始化成员列表
3、成员初始化次序
基类早于子类,成员变量以其声明次序被初始化
在初始化成员列表中,最好总是以其声明次序为次序,避免成员变量初始化带有次序性
例:初始化array时需要指定大小,因此代表大小的成员变量必须先有初值
4、不同编译单元的初始化次序
non-local static:全局或namespace作用域内,或类、文件作用域被声明为static
需要确保使用前初始化
方法:Singleton模式
1)将non-local static对象放在专属函数中,声明该对象为static(内联函数)
2)返回一个引用指向它包含的对象
3)用户调用该函数,而不是对象
因为函数内的local static对象,会在函数被调用,首次遇到该对象定义式时,被初始化
5、请记住
1)为内置型对象手工初始化
2)构造函数使用成员初始化列表,而不是在构造函数本体内使用赋值操作。同时初始化的排列次数,应该与它们在类的声明次序相同
3)为免除"跨编译单元的初始化次序"问题,以local static对象替换non-local static对象
【上篇】
【下篇】

抱歉!评论已关闭.