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. EF Codefirst方式数据库维护操作

    关于EF codefirst方式数据库维护操作 1.数据实体更新 2.打开pm - 锁定项目:MLearning.Data 3.执行命令 : add-migration [名称] 4.检查无误后,执行 ...

  2. js搞定网页的简繁转换

    对网页进行简繁字体转换的方法一般有两种:一是使用<简繁通>这样的专业软件,另外一种是制作两套版本的网页.显然,这两种方法都较为麻烦,而且专业软件一般不能用于免费的空间.笔者在这里给大家提供 ...

  3. 转Python 和C#的交互

    http://www.cnblogs.com/wilber2013/category/708919.html IronPython和C#交互   IronPython是一个.NET平台上的Python ...

  4. Oracle-EXP-00011 表不存在

    Oracle-EXP-00011 表不存在 点我,点我~

  5. ubuntu 16.04 忘记root密码

    虚拟机中安装的ubuntu 16.04. 方法一 如果用户具有sudo权限,那么直接可以运行如下命令: sudo su root #输入当前用户的密码 passwd #输入密码 #再次输入密码 方法二 ...

  6. eclipse 新项目导入到tfs 步骤

    为了下次导入项目 不动脑子,写下此步骤.... 1.右键要导入的项目>> share project(如果有这项就点它,然后 进入 分享至你的tfs服务器即可) 1.右键要导入的项目> ...

  7. bzoj3295 动态逆序对

    Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数.给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计 ...

  8. 更加灵活的编写控制层的方法____结合eval函数

    结合EVAL函数,我们可以把API放到测试用例那边去,就可以使用一个定位元素,测试用例可以使用多个API 发现eval里面可以拼接str.那么写成这样更顺眼     eval("self.d ...

  9. js中,object可以调用style对象,[]不可以调用style对象

    <!DOCTYPE HTML><html><head><meta http-equiv="Content-Type" Content=&q ...

  10. [转]批处理遍历文件夹生成 html 文件

    [转自] http://www.360doc.com/content/15/0205/20/21861372_446525665.shtml :: 自动将指定文件夹中的图片写入到 html 文件中 @ ...