条款一:视C++为一个语言联邦

为了理解C++,你必须认识其主要的次语言。幸运的是总共只有四个:

  • C:C++是由C语言继承而来的,必然对C有很好的兼容性,这一部分主要包括C中的一些语言,库函数等。但当你以C++内的C成分工作时,高效编程守则照出C语言的局限:没有模板、没有异常、没有重载。。。
  • Object-Oriented C++:这部分也就是C with Class所诉说的:classes(包括构造函数和析构函数)、封装、继承、多态、virtual函数(动态绑定)。。。。等等。
  • Template C++:这一部分主要是泛型编程。
  • STL:标准模板库,主要是以template C++为基础实现的,里面提供了很多有用的类和对应的算法,帮助我们很好的结果了C语言中要自己去解决的问题,主要有容器(数组就是一个特殊的容器)、迭代器(智能指针,之所以用迭代器是为了通用性)、算法(包括容器特殊的算法和容器间通用的算法)、函数对象(能想函数一样被调用的对象,通过重定义对象中的()操作符来完成的)、适配器(可以理解为修改了容器接口实现的一种容器),这一部分是c++强大的后盾,学习C++。不能不学STL,不仅会用最好能知道STL中成员的实现方式,这样就能更加高效的使用!
条款二:尽量以const,enum,inline替换#define
       可以理解为能在编译器期间做的事情不要放到预编译器中处理,预编译器是对程序编译之前的一个预先的处理,不会检查对代码最任何的检出,这样如果在编译期间发现问题,如果问题是我们自己直接造成的那可能会很快的定位,想反的如果问题在预编译期间做了一些处理,那样在编译中出现的问题可能会显示预编译中的问问题,这样对问题定位不方便,例如:在程序中定义宏#define PI 3.1415,如果编译过程中出现错误,错误显示中会直接显示3,1415,而不会对PI符号有所说明,如果宏是自己定义的还好,如果是引用的其他头文件中的定义,那真的不是很好查的!对于条款二,主要有以下两点说明:
1.尽量以const、enum定义来替换#define 定义。

在头文件中以#define定义的常量在预编译期间直接对对应的符号进行替换,没有内存的申请,这也是#define可以放到头文件中的原因,对于const常量定义中,它将常量直接放入到符号表中,不会申请固定的内存,除非对该常量有内存的引用,这也就是为什么const定义的常量能像#define一样放到头文件中,在const常量的使用中主要常量重叠的出现,相关知识自行查阅!

       为了将常量的作用域限制于class内,你必须让它成为class的一个成员;而为确保此常量至多只有一份实体,你必须让它成为一个static成员:
class GamePlayer{
private:
static const int NumTurns = 5;//常量声明式
int score[NumTurns];//使用该常量
.....
}

上面中NumTurns是常量的声明而不是定义式,在后面的使用中如果只是使用该常量并没有用到该常量的地址,就可以不用再对该常量做定义,否组需要如下的定义:

const int GamePlayer::NumTurns;

请把这个式子放进一个实现文件而非头文件。由于class常量已在声明时获得初值,因此定义时不可以再设初值。

       对应的在有些编译器中是不支持在声明常量的时候直接赋值的,例如:
class GamePlay{
private:static const int NumTurns;//static class常量声明位于头文件内
.......
}

后面接着定义:

const  int  GamePlay::NumTurns=5;//static class常量定义位于实现文件内

enum
1  比较像#define而不像const。取一个enum的地址不合法,所以可以防止pointer或reference指向你的某个整数常量。
2  模板元编程的基础技术

Template Inline代替宏

#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函数来替换:

template<typename T>
inline void callWithMax(cosnt T &a, cosnt T &b)
{
f(a > b ? a : b);
}

其中用到了template模板函数,之所以用引用时为了自定义的类型,如果只是内置类型,可以不需要引用!

  • 对于单纯常量,最好以const对象或enums替换#defines;
  • 对于形似函数的宏,最好改用inline函数替换#defines。

条款三:尽量的使用const
1.const对于基本内置类型的约束,主要指的是指针类型。
const int * pi;
int * const pi;

在STL中,迭代器也是一种指针,const可以对迭代器进行修饰,对应的也有两种修饰的方式:

const std::vector<int>::iterator iter;
std::vector<int>::const_iterator iter;
       此外在函数的应用中,也常用到const关键字,主要是两点,一是对于函数的形式参数的修饰上,如果参数在函数内部不被修改那么一般情况下都要用const将参数修饰下,这样当函数接口暴露出去的时候,别人能很容的看清楚,还有一种比较少见的用法就是对函数返回值得修饰上,这个主要是防止最函数返回值进行赋值操作,例如下面:
class Ration{};
const Ration operator*(const Ration& lhs,const Ration* rhs);
2.const成员函数
class Ration{
private:
int n_;
public:
int getn(){
return n;
}
int getn() const{
return m;
}
void setn(int i) const{
n = i;
}
};

在C++中有函数重载的概念,对同名的函数,如果函数的参数类型或者个数不相同,就可以作为不同的函数,这个主要是通过在编译源代码过程中对不同的函数重新命名来实现,对于const修饰的函数,不能对对象的任何成员进行修改,并且const修饰的对象只能调用对象的const成员函数,其中const对象主要是作为函数的参数进行传递的!

class Ration{
private:
int n_;
public:
int getn(){
return n;
}
int getn() const{
return m;
}
void setn(int i) const{
n = i;
}
};

定义一个const Ration test,则test调用getn()函数的时候只能调用const的get函数,不能调用普通的成员函数,如果没有const的成员函数,将报错,对应的例子中的setn函数定义为const函数,但是它却对成员n进行了赋值,因此是不允许的,编译也不会通过,如果想让setn()函数编译通过,我们可以借助关键词mutable,将n定义为mutable int n,这样即使在const函数中也可以对n进行修改!咱们一般的应用中很少直接定义一个const的对象,一般const的对象是用在函数的形式参数中出现的!

       当const成员函数与非const成员函数功能相同的时候,我们一般不会定义两个成员函数,其中一个只是比另一个多了一个const的修饰,我们的解决办法是让非const的成员函数调用const的成员函数,其中const的成员函数正常定义,此时可能用到C++中的强制类型转换例如static_cast/const_cast等!
条款四:确定对象在使用前已经初始化
       对于内置类型以外的任何其他东西,初始化责任落在构造函数(constructors)身上。确保每一个构造函数都将对象的每一个成员初始化。
       C++规定,对象的成员变量的初始化动作发生在进入构造函数本体之前。
       初始化列表免于先调用 default构造函数然后再调用赋值操作符,只需调用一次拷贝构造函数。更高效。
       构造函数的最佳写法是,使用 member initialization list(成员初始化表)如:
ABEntry::ABEntry(char&name,char& address,list &phones)
:theName(name),theAddress(address),thePhones(phones)
{
…….
}
  • 编译器会为用户自定义类型(user-definedtypes)之成员变量自动调用default构造函数--- 如果那些成员变量在“成员初始化列表”中没有被指定初值的话。

  • 成员变量是 const 或references,它们就一定需要初值,不能被赋值。

  • C++有着十分固定的“成员初始化次序”。Base classes 更早于其derived classes 被初始化,而class的成员变量总是以其声明次序被初始化。

  • Static 对象,其有效时间从被构造出来直到程序结束为止,因此stack和heap-based对象被排除。

注意:
  • 为内置对象进行手工初始化,因为C++不保证初始化它们;

  • 构造函数最好使用成员初始化列表,而不要在构造函数本体内使用赋值操作。初始化列表列出的成员变量,其排列次序应该和它们在类中的声明次序相同;

  • 为免除“跨编译单元之初始化次序”问题,请以local static对象替换non-local static对象。

Effective C++ ——让自己习惯C++的更多相关文章

  1. Effective C++ —— 让自己习惯C++(一)

    条款01 : 视C++为一个语言联邦 C++ == C(C基本语法) + Object-Oriented C++(类,封装,继承,多态……) + Template C++(泛型编程) + STL(容器 ...

  2. Effective C++ —— 构造/析构/赋值运算(二)

    条款05 : 了解C++默默编写并调用哪些函数 编译器可以暗自为class创建default构造函数.copy构造函数.copy assignment操作符,以及析构函数. 1. default构造函 ...

  3. C++易混淆知识点整理

    // 1 /////////////////////////////////////////////////////////////////////// // 常量指针:,指针可修改,变量不可修改(只 ...

  4. #pragma init_seg

    先进后出原则,最先初始化的最后析构! 1.C++中全局对象.变量的构造函数调用顺序是跟声明有一定关系的,即在同一个文件中先声明的先调用.对于不同文件中的全局对象.变量,它们的构造函数调用顺序是未定义的 ...

  5. [.NET] 《Effective C#》快速笔记(一)- C# 语言习惯

    <Effective C#>快速笔记(一)- C# 语言习惯 目录 一.使用属性而不是可访问的数据成员 二.使用运行时常量(readonly)而不是编译时常量(const) 三.推荐使用 ...

  6. 《Effective C#》快速笔记(一)- C# 语言习惯

    目录 一.使用属性而不是可访问的数据成员 二.使用运行时常量(readonly)而不是编译时常量(const) 三.推荐使用 is 或 as 操作符而不是强制类型转换 四.使用 Conditional ...

  7. 《Effective C++》第1章 让自己习惯C++-读书笔记

    章节回顾: <Effective C++>第1章 让自己习惯C++-读书笔记 <Effective C++>第2章 构造/析构/赋值运算(1)-读书笔记 <Effecti ...

  8. Effective C++(第三版)笔记 ---- 第一部分让自己习惯C++

    内容从侯捷译版的<Effective C++>(第三版)摘录 条款一 C++作为一个多种范式融合的语言,可以看成是语言的联邦,它包含了一下四种主要的次语言: C.C++以C为基础,很多时候 ...

  9. seven habits of highly effective people 高效能人士的七个习惯

    习惯的模型 : dependent 依赖  -- independent 独立自主 --interdependent  互相依赖 1: be  proactive 主动积极 what you can ...

随机推荐

  1. Spring--bean的作用范围

    在Spring中,bean的作用范围分以下几种: singleton:spring ioc容器中仅有一个bean实例,bean以单例的方式存在 prototype:每次从容器中调用bean时,都返回一 ...

  2. 计蒜客模拟赛5 D2T1 成绩统计

    又到了一年一度的新生入学季了,清华和北大的计算机系同学都参加了同一场开学考试(因为两校兄弟情谊深厚嘛,来一场联考还是很正常的). 不幸的是,正当老师要统计大家的成绩时,世界上的所有计算机全部瘫痪了. ...

  3. P3928 SAC E#1 - 一道简单题 Sequence2

    题目背景 小强和阿米巴是好朋友. 题目描述 小强喜欢数列.有一天,他心血来潮,写下了三个长度均为n的数列. 阿米巴也很喜欢数列.但是他只喜欢其中一种,波动数列. 阿米巴把他的喜好告诉了小强.小强便打算 ...

  4. Go学习——defer、panic

    defer: 延迟到ret之前,通常用于IO的关闭 or 错误处理. 在延迟出现的异常可以被后面的捕捉,但是只有最后一个. defer可以多次,这样形成一个defer栈,后defer的语句在函数返回时 ...

  5. ●BZOJ 3894 文理分科

    题链: https://vijos.org/d/ljt12138/p/58c696b8d3d8a16c62a248d4 (要权限号啊...用这个交吧) 题解: 题目大意:    N*M的矩阵,每个位置 ...

  6. hdu5601 BestCoder Round #67 (div.2)

    N*M bulbs  Accepts: 94  Submissions: 717  Time Limit: 10000/5000 MS (Java/Others)  Memory Limit: 655 ...

  7. BZOJ1095(动态点分治+堆)

    终于把这个坑填了.. 按重心分治建树,每个点存两个堆,第一个存的是这个点子树中的点到父重心的距离,第二个存的是子节点第一个堆的堆顶,同时有一个全局答案堆,存的是每个点第二个堆的最大值+次大值. 20亿 ...

  8. 动态规划--Kin

    动态规划: 1.最大子序列和 2.LIS最长递增子序列 3.LCS最长公共子序列 4.矩阵连乘 5.数字金字塔 1.最大子序列和 #include<iostream> using name ...

  9. 基于GCC的openMP学习与测试

    (一).openMP简述 Open Multiprocessing (OpenMP) 框架是一种功能极为强大的规范,可以帮助您利用 C.C++ 和 Fortran 应用程序中的多个核心带来的好处,是基 ...

  10. session.save()返回值问题

    正常都应该返回插入的主键 但是 如果你用sessionFactory来写就一定返回0 先科普下持久化数据库的三个状态方便下面理解 一次会话状态中,持久化对象经历以下三种状态:1 transient:对 ...