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. FastAdmin composer json 版本说明

    来源于 FastAdmin 执行 composer update 后将 ThinkPHP 升级到了 V5.1. FastAdmin  是基于 ThinkPHP 5.0.x 开发的,而 ThinkPHP ...

  2. O(n)线性空间的迷宫生成算法

    之前所有的迷宫生成算法,空间都是O(mn),时间同样是O(mn),时间上已经不可能更优化, 于是,我就从空间优化上着手,研究一个仅用O(n)空间的生成算法. 我初步的想法是,每次生成一行,生成后立即输 ...

  3. WIN7\win10下使用批处理配置JAVA环境变量

    我找了很多环境变量批处理的教程,都不太满意,因此综合修改了下,拼凑出了这么一个版本. 下面这个是我主要参考的博客,大部分的代码都是来自这里: http://blog.csdn.net/lpy36543 ...

  4. oracle_外部表的两种实现方式oracle_loader[datapump]

    外部表可以实现,通过数据库对象直接访问目录文件里的格式数据,加载方式分为两种oracle_loader和oracle_datapump,oracle_loader方式通过sqlldr引擎方式加载,访问 ...

  5. ubuntu初次设置root密码

    初次安装ubuntu创建的用户不是root用户,但是需要root权限的时候又需要密码,那么如何设置密码呢? 很简单.如下几步操作

  6. 【FusionCharts学习-1】获取资源

    网址 官网: http://www.fusioncharts.com/charts/  入门学习:http://www.fusioncharts.com/dev/usage-guide/getting ...

  7. 【整理总结】代码沉淀 - CefSharp - 比较流行的第三方内嵌浏览器组件

    .NET (WPF and Windows Forms) bindings for the Chromium Embedded Framework web: https://github.com/ce ...

  8. cookie js案例

    //存cokie function setcookie(keys,value,time){ document.cookie=keys+"="+decodeURIComponent( ...

  9. nsenter工具进入docker容器

    对于运行在后台的Docker容器,我们经常需要做的事情是进入到容器中,docker为我们提供了docker exec .docker attach 命令,并且还提供了nsenter工具,外部工具供我们 ...

  10. 6.28笔记-servlet3.0注解配置、文件上传、过滤器、监听器

    一.servlet3.0注解配置 使用javaEE6.0 支持servlet3.0 value的值就是访问路径 urlPatterns的值也是访问路径 @WebServlet(name="D ...