Essential C++读书笔记

Essential C++读书笔记

  • 1 c++编程基础
  • 2 面向过程的编程风格
  • 3 泛型编程风格
  • 4 基于对象的编程风格
  • 5 面向对象编程风格
  • 6 以template进行编程
  • 7 异常处理

1 c++编程基础

  • 如果没有在main()的末尾写下return语句,这一语句会自动加上
    return 0 表示 mian()返回0,表示程序执行成功。
int main()
{
	return 0;
}
  • 对象不能以数字开头
  • 被定义为const对象,在获得初值之后,无法再次改动。
const int max = 24;//correct
max = 12;//error
  • switch中,default后加break;
  • C++内置的数组 Array 的大小 必须是常量表达式
const int size = 10;
int seq[size];
  • 可以用array初始化vector
  • 指针具有双重性质:既可以操作指针包含的地址,也可以操作指针所指向的值。
  • cstdlib头文件有rand()和srand(),配合使用产生伪随机数序列

2 面向过程的编程风格

  • 如果用户输入不合理的值,最极端的是终止程序exit(),或者是抛出异常(第七章)
if (post <= 0)
	exit(-1);
  • 函数声明可以不写出参数名称:小规模程序中好用
#include 
bool func(int, int);
int main()
{
	//......
}
bool func(int a, int b)
{
	return (a == b) 
}
  • 两种函数参数传递方式 : 引用传递(by reference)和值传递(by value)
  • 值传递:传给函数的参数被复制了一份到程序堆栈的局部中,原参数与副本之间没有任何关联。一旦完成函数,这块内存就被释放掉,pop出。
  • 引用传递:面对引用的参数的所有操作都与原参数所进行的操作一致。如下所示:
void display(vector<int> &a)
  • 加上const,可以让阅读人知道,以引用传递的方式传递vector,目的是避免复制操作,而不是为了要在函数中对它进行修改。
void display(const vector<int> &a)
  • 也可以将vector以指针形式传递,传递的是地址,而不是副本
void display(const vector<int> *a)
  • 除非你希望在函数内修改参数值,否则不要使用引用方式
  • 在下面代码,无论以指针还是引用将elems返回,都不对,因为函数执行完之后,所指向或引用的对象不存在了。若以值传递方式返回,则没问题,因为返回的是值的副本,它在函数之外仍然存在。
vector<int> func(int size)
{
	vector<int> res;
	...
	return res;
}
  • 上述的操作:大多数c++编译器“以传值返回的类对象”,会优化程序,加上额外的引用参数。参考【LIPPMAN98】14.8
  • 内存泄漏:程序员不用delete则由heap堆分配的对象永远不会释放。
  • 局部静态对象static所处的内存空间,即使在不同的函数调用中,依然存在。如下:
const vector<int>* func(int size)
{
	static vector<int> elems;
	return &elems;
}
  • 编译器无法通过函数返回类型区分两个具有相同名称的函数
  • 重载函数的参数列表必须和其他重载函数的不同。
  • 如果函数具有多种实现,可以将它重载,希望代码主体不变,仅仅改变数据类型,可以通过函数模板Function Template
  • 函数的定义只能有一份,不过可以有许多声明。
  • 唯一例外,inline函数的定义。将其放到头文件,每个调用点上都进行了定义。

3 泛型编程风格

  • STL主要由两种组件组成:一是容器,二是操作容器的泛型算法。
  • vector和list是顺序性容器:主要进行迭代
  • map和set是关联容器:快速查找元素的值
  • 被称为泛型,因为操作与元素类型无关,不直接操作容器,而是使用一对迭代iterator(first和second)
for (iterator it = vec.begin(); it != vec.end(); it++)
		cout << *it << endl;
  • 对于const vector,可以使用const_iterator来进行遍历,允许我们读值,但不允许写入。
const vector<int> vec;
vector<int>::const_iterator it = vec.begin();
  • 若vec为空,则iter等于end();
  • 对容器的迭代操作,始于begin()而终于end()
  • 使用泛型算法,首先得包含对应的algorithm头文件
  • 使用sort()函数,可以自己定义比较的函数
  • adapter(适配器)将函数参数绑定至某值,如二元函数-》一元函数
  • 查询某key是否存在于map,有三种方法:
  1. 把key当做索引使用 (如果key不存在,则会加入map中,而相应的key会设定为默认值)
int count = 0;
if (!(count = map["ws"]))
  1. 利用map中的find(),若存在,返回iterator指向该值(此find与泛型算法find不同)
map<string, int> test;
map<string, int>::iterator it;
it = test.find("ws");
if (it != test.end())
	int count = it->second;//first key    second value
  1. 利用map的count()函数 (返回特定key1的个数)
if (test.count("ws")) //存在则为true
  • set为集合

4 基于对象的编程风格

  • 成员初始化列表 紧跟着参数列表后面,以逗号分隔的列表
animal::speak(string words): _age(age), _sex(sex)
{
	//......
}
  • 析构函数无返回值也不可能被重载
  • 若构造函数使用new表达式从堆中分配数组,则在析构函数中释放这些内存。
  • 成员逐一初始化
animal cat(..., ...);
animal dog = cat;

若cat中含有堆分配的数据,构造中new,析构中delete
此时将cat赋值给dog,dog析构会将释放内存,此时cat中仍然可能使用改数据,发生错误。

  • 复制构造函数,参数是const reference
animal::animal(const animal &ani)

产生一个独立的数据副本,这样使得某个对象的西公园不会影响另一个对象。

  • mutable 与 const
  • const紧跟在函数参数列表之后,使该函数明白,不能修改调用者的值
animal::animal(const animal &ani)
{
	string name = ani.name();
}
int animal::name() const {} //因为ani为引用,name函数不能修改ani的值
  • 非const 类型的参数,会调用非const版本的成员函数
  • const 类型的参数,会调用const版本的成员函数
  • murtable 类型可以使得成员函数既可以修改,又可以被声明为const函数
  • this指针在成员函数中指向其调用者(一个对象)
  • 返回对象可以使用*this
animal& animal::speak(const animal &ani)
{
	//......
	return *this;
}
  • 运算符重载

不可以引入新运算符
操作个数不可变
运算符优先级不变
参数列表中必须至少一个为class

  • 友元friend的建立,通常是为了考虑效率。
  • 如果只是希望进行某个数据的读取与写入,那么为他提供具有public访问权限的inline函数,就是建立友元friend的替代方案。

5 面向对象编程风格

  • 继承和多态

  • 父类(基类)和子类(派生类)

  • 父类定义了所有子类的公有接口和私有实现。每个子类可以增加或者覆盖继承来的东西。

  • 动态绑定 第三个独特概念,如依据父类对象所指向的子类对象来决定调用哪个成员函数。

  • 多态让我们以一种与类型无关的方式来操作类对象

  • 多态和动态绑定的特性,只有在使用指针或者引用是才发挥。

  • 成员函数动态绑定,需要加上virtual

  • 定义抽象基类第一个步骤是找出所有子类的共同特性

  • 虚函数=0,则为纯虚函数

  • 只要类声明有一个或多个纯虚函数,由于其接口的不完整性(纯虚函数没有定义)程序无法产生任何对象。不能实例化

  • 定义派生类,一个是基类构成的子对象,另一个是派生类的部分。

  • 类进行继承声明之前,其基类定义必须已经存在。

  • 子类必须为每个纯虚函数提供实现。

  • 已经指向或者引用子类的父类对象,无法调用子类的非基类提供的函数接口。

    解决方法:

  1. 在基类中加入virtual 函数,子类之前的派生出来的非基类接口重写虚函数
  2. 将这部分接口移动至基类。
  • 每当派生类中有某个成员与基类重名,则覆盖基类的那份成员。

  • 如果要在派生类内使用继承来的那个成员,使用::作用域解析符限定

  • 较好的设计方式,基类提供构造函数,并完成基类所声明的所有数据成员操作。

  • 派生类的初始化,包括基类的构造函数以及自己的构造函数

  • 定义派生类时,必须决定,将基类中的虚函数覆盖掉哈市原封不动的加以继承。继承了纯虚函数免责这个派生类也成为了抽象类。

  • 在派生类中,重写virtual 函数时,关键字virtual可以不写

  • 虚函数静态解析:此两种情况下,虚函数不会出现预期行为

    1. 基类构造函数与析构函数(调用虚函数,为自己的函数)
    2. 当使用基类对象,而非基类对象的指针或者引用时。(多态需要一层间接性,唯有使用基类的指针或引用才能支持面向对象编程)
  • 类型鉴定机制,typeid运算符

6 以template进行编程

7 异常处理

你可能感兴趣的:(C++学习)