1.当我们看到赋值符号时,请小心,因为"="也可以用来调用copy构造函数
Widget w3 = w2; //调用copy构造函数,而不是copy赋值操作符

2.不明确的行为:
int *p = 0; // p是个null指针
cout<<*p; // 对一个null指针取值是不明确的行为

3.常量定义式通常被放在头文件中,以便被不同的源码含入。
有两种特殊情况需要注意:
(1)定义常量指针
有必要将指针(而不是指针所指之物)声明为const。 const char* const authorName = "Bob";
(2)class的专属常量
为了将常量的作用域限制于class内,你必须让它成为class的一个成员;而为确保此常量最多只有一份实体,你必须让它成为一个static成员。
class GamePlayer{
private:
static const int NumTurns = 5; // 常量声明式
int scores[NumTurns]; // 使用该常量
}
注意:我们看到的NumTurns是声明式而非定义式。 通常C++要求你对你所使用的任何东西提供一个定义式,但如果它是个class专属常量又是static且为整数类型(如int,char,bool),则需特别处理。
只要不取它们的地址,你就可以使用它们而无需定义式。
如果我们要取某个class专属常量的地址,你就必须提供如下定义式:
const int GamePlayer::NumTurns; // 把各个式子放到实现文件而非头文件。由于class常量声明时就获得初值,所以定义时不需要再设初值。

4. the enum hack补偿
如果你的编译器不允许“static整数型class常量”完成类内初值设定,
class GamePlayer{
private:
static const int NumTurns = 5; // 常量声明式,但有的编译器不允许此种类型在类内设置初值
int scores[NumTurns]; // 使用该常量
}

那么可以这样:
class GamePlayer{
private:
enum { NumTurns = 5 }; // "the enum hack"令NumTurns成为5的一个记号名称。
int scores[NumTurns];
}

5.对于形似函数的宏,最好改用inline函数代替#define
因为预处理定义出来的函数,经常逻辑混乱,容易出错。

6.const面对函数声明时的应用。在一个函数声明式内,const可以和函数返回值、各参数、函数自身产生关联。
(1)令函数的返回值为cosnt 考虑如下:
class Rational {...};
const Rational operator*(const Rational &lhs,const Rational &rhs);
为什么返回一个const对象呢? 原因是为了防止这样一些暴行:
Rational a,b,c;
(a*b) = c; // 客户可能是想写(a*b) == c 进行比较行为,但手误写成了一个赋值。这种动作是无意义的。
所以,我们声明函数的返回值为const,预防了这种无意义的行为。
(2)令函数本身为const(即const成员函数)
const成员函数之所以如此重要是因为:
第一,它使得class接口更加清晰:我们很容易知道哪个函数可以改动对象内容而哪个函数不行。
第二,因为const对象只能调用const成员函数。所以,它使得操作const对象成为可能。

mutable的使用。有时const成员函数需要改变对象的某一些数据,但这又违反了const成员函数的规则,通过将这些数据声明为mutable改善这种情况。

当const和non-const的版本有着等价的实现时,用const版本来实现non-const版本来避免代码重复。 如:

class TextBlock{
public:
...
const char& operator[](size_t position) const
{
...
return text[position];
}
char& operator[](size_t position)
{
return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);
}
}

7.确定对象被使用前已被初始化
我们知道,使用了未被初始化的对象,将会导致未定义的行为。
最好的办法是使用初始值列表进行初始化,这比在构造函数体中的赋值行为更为高效,而且避免了对象的成员是const或引用引发的问题。
(因为成员变量是const或reference一定需要初值而不能被赋值。)
有时候,对象的数据成员太多了,放在初始值列表中太长,我们可以将这些成员中的内置类型的成员的初始化放在一个函数中(通常是private的),供所有构造函数调用。

C++有十分固定的初始化顺序,基类,然后派生类。而成员也是按照声明的顺序初始化的,与它们在初始值列表的顺序无关。
尽管如此,还是一个地方需要注意:当使用一个编译单元中的外部变量来初始化另一个编译单元的变量时,它用到的这个变量可能未被初始化。
因为C++对于定义于不同编译单元的对象的初始化次序并无明确定义。

eg:
//FileSystem.cpp
class FileSystem{
....
size_t numDisks() const;
...
};
extern FileSystem tfs;

//Directory.cpp
class Directory{
public:
Directory( params );
...
};
Directory::Directory(params)
{
...
size_t disks = tfs.numDisks(); // 使用tfs对象
...
}

Directory tempDir(params); //创建一个对象

这时,除非tfs在tempDir之前先被初始化,否则tempDir的构造函数将用到未初始化的tfs

有一种类似单例模式的方法来解决这个问题:
class FileSystem{...};
FileSystem &tfs() // 将这个对象用类似这种的函数代替。由于函数体很简单,非常适合定义为inline函数
{
static FileSystem fs;
return fs;
}

class Directory{...};
Directory::Directory(params)
{
...
size_t disks = tfs().numDisks();
...
}

原理是:函数内的static对象会在该函数调用期间,首次遇到该对象的定义式时被初始化。(static对象会在首次用到它的地方进行唯一一次的初始化)
这样,我们就保证了返回的引用永远是一个已经初始化了的对象。

8.当我们的类没有显式定义copy构造函数、copy赋值运算符、析构函数时,编译器会自动为我们定义。
但并不总是这样。考虑如下:
class NamedObject{
public:
NamedObject(string &name,const int &value); // 构造函数
... //未声明operator=
private:
string &nameValue; // 引用
const int objectValue; // const对象
}

string newDog("Bob");
string oldDog("Tony");
NamedObject p(newDog,2);
NamedObject s(oldDog,36);

p = s; // 发生什么?

这段代码将编译错误,我们将s赋值给p,由于C++不允许让引用指向不同的对象,所以,编译器不知道生成什么样的operator=来完成这个赋值,报错。
同样,const的对象也不允许指向其他对象。
如果你打算在一个包含引用、const成员的类内支持赋值操作,就必须自己定义copy赋值运算符,而不能指望编译器生成。

还有一种情况是,如果基类将copy赋值运算符声明为private,则编译器拒绝为其派生类生成一个copy赋值运算符。
因为派生类生成的copy赋值运算符想象中可以处理基类中的成员,但基类中的copy赋值运算符是私有的,它们无法调用。

9.我们知道,通过把copy构造函数、copy赋值运算符声明(而不定义)为private,可以阻止拷贝动作,包括成员函数和友元函数也不行。
如果试图拷贝将会导致运行时的链接错误(linkage error)。
我们可以将链接期错误移至编译期:通过将copy构造函数、copy赋值运算符在一个专门为了阻止拷贝动作的基类内声明(而不定义)为private来完成。
如:
class Uncopyable{
protected:
Uncopyable(){}
~Uncopyable(){}
private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);
};

为了阻止Foo对象被拷贝,我们唯一要做的是继承Uncopyable:
class Foo : private Uncopyable{
....
};

Effective C++笔记——day01的更多相关文章

  1. Effective Java笔记一 创建和销毁对象

    Effective Java笔记一 创建和销毁对象 第1条 考虑用静态工厂方法代替构造器 第2条 遇到多个构造器参数时要考虑用构建器 第3条 用私有构造器或者枚举类型强化Singleton属性 第4条 ...

  2. [Effective JavaScript 笔记] 第4条:原始类型优于封闭对象

    js有5种原始值类型:布尔值.数字.字符串.null和undefined. 用typeof检测一下: typeof true; //"boolean" typeof 2; //&q ...

  3. [Effective JavaScript 笔记] 第5条:避免对混合类型使用==运算符

    “1.0e0”=={valueOf:function(){return true;}} 是值是多少? 这两个完全不同的值使用==运算符是相等的.为什么呢?请看<[Effective JavaSc ...

  4. [Effective JavaScript 笔记]第3章:使用函数--个人总结

    前言 这一章把平时会用到,但不会深究的知识点,分开细化地讲解了.里面很多内容在高3等基础内容里,也有很多讲到.但由于本身书籍的篇幅较大,很容易忽视对应的小知识点.这章里的许多小提示都很有帮助,特别是在 ...

  5. [Effective JavaScript 笔记]第27条:使用闭包而不是字符串来封装代码

    函数是一种将代码作为数据结构存储的便利方式,代码之后可以被执行.这使得富有表现力的高阶函数抽象如map和forEach成为可能.它也是js异步I/O方法的核心.与此同时,也可以将代码表示为字符串的形式 ...

  6. [Effective JavaScript 笔记]第28条:不要信赖函数对象的toString方法

    js函数有一个非凡的特性,即将其源代码重现为字符串的能力. (function(x){ return x+1 }).toString();//"function (x){ return x+ ...

  7. java effective 读书笔记

    java effective 读书笔记 []创建和销毁对象 静态工厂方法 就是“封装了底层 暴露出一个访问接口 ” 门面模式 多参数时 用构建器,就是用个内部类 再让内部类提供构造好的对象 枚举 si ...

  8. Effective STL 笔记 -- Item 6 ~ 7: Container and Object Pointer

    Effective STL 笔记 – Item 6 ~ 7: Container and Object Pointer 中间两次笔记被删掉了,简单补一下: Item 3 中提到如果将对象直接放入容器中 ...

  9. Item 5:那些被C++默默地声明和调用的函数 Effective C++笔记

    Item 5: Know what functions C++ silently writes and calls 在C++中,编译器会自己主动生成一些你没有显式定义的函数,它们包含:构造函数.析构函 ...

随机推荐

  1. actor model vs tasked based parallizm

    举例子:计算pi actor model概念:一般有n个actor(task),和一个调度线程(本身也是一个actor)调度线程负责向每个task发送命令执行计算,以及接收每个task的结果并归并到一 ...

  2. .csv 和 .xls 的区别

    .csv 和 .xls 的区别 .csv .xls 较为通用,易导入至各式表格.资料库等 Microsoft excel的专用档案 文本档案,用记事本就可以打开 二进位档案,只有用excel才能打开 ...

  3. 大快DKH大数据智能分析平台监控参数说明

    2018年国内大数据公司50强榜单排名已经公布了出来,大快以黑马之姿闯入50强,并摘得多项桂冠.Hanlp自然语言处理技术也荣膺了“2018中国数据星技术”奖.对这份榜单感兴趣的可以找一下看看.本篇承 ...

  4. python下爬某个网页的图片

    #coding=utf-8 import re import urllib def getHtml(url): #获取url对应得源码 page = urllib.urlopen(url) html ...

  5. 【python】class之super关键字的作用

    在Python类的方法(method)中,要调用父类的某个方法,在Python 2.2以前,通常的写法如代码段1: 代码段1: class A: def __init__(self):    prin ...

  6. linux svn安装(转载)

    第一章 安装 1. 采用源文件编译安装.源文件共两个,为:subversion-1.6.1.tar.gz (subversion 源文件)subversion-deps-1.6.1.tar.gz (s ...

  7. Spring Cloud config之一:分布式配置中心入门介绍

    Spring Cloud Config为服务端和客户端提供了分布式系统的外部化配置支持.配置服务器为各应用的所有环境提供了一个中心化的外部配置.它实现了对服务端和客户端对Spring Environm ...

  8. Dell PowerEdge R630服务器VMware ESXI6.0服务器安装

    一.Raid 磁阵划分 官方说明:http://www.dell.com/support/article/cn/zh/cndhs1/SLN292050/%E6%88%B4%E5%B0%94%E6%9C ...

  9. Appium+python自动化8-Appium Python API

    Appium+python自动化8-AppiumPython API   前言: Appium Python API全集,不知道哪个大神整理的,这里贴出来分享给大家. 1.contexts conte ...

  10. Frequently Asked Questions

    转自:http://www.tornadoweb.org/en/stable/faq.html Frequently Asked Questions Why isn’t this example wi ...