沈显君 C++语言程序设计教程 第3版 总结
总结于:沈显君 C++语言程序设计教程 第3版
1.类
1.1 类与成员
class与struct:class中的成员变量默认为private。protected代表成员可以被该类和其子类存取,但是类的对象是无法直接访问protected成员的
类的成员变量可以定义为该类的指针和引用,不能定义该类的变量。成员函数中可以该类的变量(对象)。
类外和类内(以时间换空间)声明成员函数。类外用“类名::”指明成员函数属于哪个类。
对象共享成员函数的定义。
定义为protected 等级的成员可以被该类及其子类存取。
1.2 构造函数与析构函数
构造函数:给数据成员赋初值。函数名与类名相同,没有返回值和void,为public类型的。可以用多个构造函数
析构函数:如释放在构造函数中申请的内存。函数名为类名前加~,没有返回值和void。只能有一个析构函数。
拷贝构造函数
用一个对象初始化一个新建立的对象。用于初始化对象、作为实参、作为返回值。
四个注意点
深拷贝构造函数:如开辟内存,存储原对象中的指针指向的数据
1.3 对象的指针和引用
引用作为函数形参是最好的
使用new和delete建立动态对象
this指针指向的是当前对象的地址(不常用)。
组合对象:含有其他类的对象作为数据成员的类,此类声明的对象叫做组合对象。
组合类中的对象成员以其在类中声明的先后顺序依次构造,而不是根据构造函数初始化列表中成员对象 构造函数声明的先后顺序来决定。
1.4 静态成员:为所有对象所共享的成员
类内声明、类外初始化。
静态成员函数:静态成员函数主要为了调用方便,不需要生成对象就能调用。静态成员函数中不能调用非静态成员。非静态成员函数中可以调用静态成员。
1.5 友元函数
友元函数是在类内定义或声明的普通函数,友元函数中创建的对象可以访问private和protect类型的数据成员。
友元类:将A声明为B的友元类,那么A成员函数中创建的B类对象b,b可以访问private和protect类型的数据成员。
1.6 常成员与常对象
利用const进行修饰的不可更改的量(代表此量只读)。
作用:函数实参的保护(此量只读)
常对象:由构造函数进行初始化后就不能修改了,对象只能调用常成员函数,常成员函数中不能对数据成员进行修改。
常数据成员:类中的常数据成员只能通过构造函数的初始化列表进行初始化
常成员函数:函数声明和具体定义的地方都要带const。不能更新对象的数据成员,也不能调用该类中没有用const修饰的成员函数。
对象的内存分布。
6.继承和派生:基类派生子类(派生类),子类继承基类特性
继承的作用:更清楚地表达类与类之间所具有的共同性和差异性、增加代码的可重用性
子类中同名函数的覆盖的作用:对基类函数进行限制、改造或扩充,使得暴露的接口(函数名)一样但功能有所不同
继承和组合:没看懂????、
公有继承:派生类成员无法访问私有成员。基类的公有成员在派生类中仍然为公有成员。保护成员在派生类中仍然是保护成员。派生类成员无法访问私有成员,这就是为什么要有protected,protected就是让子类可以访问父类中受保护的成员。派生类和父类的对象都无法直接访问protected的成员,即成员是受保护的。
私有继承:经过私有继承之后,所有基类的成员都成为派生类的私有成员或不可访问的成员
保护继承:基类的公有成员和保护成员被继承后作为派生类的保护成员。派生类成员无法访问私有成员。
6.3 派生类的构造与析构
构造函数:利用构造函数初始化列表对基类进行初始化。基类有自定义构造函数,子类也就需要定义。
析构函数:负责对新增的非对象成员进行清理
6.5 多继承与二义性
多继承:子类有两个或两个以上的父类。
多继承的构造函数调用顺序:基类的构造函数,成员对象的构造函数,子类的构造函数。析构函数与此顺序相反
二义性问题:多个基类中拥有同名的成员A,子类调用A时编译器无法确定调用的是哪个A。
二义性解决:1)利用类的作用域分辨符 2)派生类中重定义此函数 3)将部分基类中的A改名
6.6 虚基类
间接二义性:
子类继承父类中成员是将成员复制一份。
间接二义性:当B和C都继承了A,然后D又继承了B和C。如果A中有成员a,那么D中就有两个不同的a。
虚基类:虚基类就是让当B和C都“虚”继承了A,B和C共同维护一个副本。
7多态:一个值取不同数据类型就叫多态。
多态:给同一函数名输入不同类型对象时,会自动调用不同处理函数,得到不同结果。虽然函数名相同但是处理的对象不同,实现的功能也只是类似而不相同。
大话设计模式中我对多态的理解:由于所有的运算都继承于基类A,所以所有运算的对象都可以用基类的对象的指针a进行指向。当a调用方法B时,不同子类的方法B的实现是不一样,同样使用a->B得到的效果是不同。多态指的就是:使用相同的代码a->B却可以根据a具体指向的对象而实现出不同的效果,这个不同的效果就是多态。
联编:确定函数输入的是什么类型的对象?
运算符重载:运算符重载相当于定义函数名相同但是形参不同的函数。编译器会根据输入函数的实参判断需要调用的函数。
运算符重载方式:
重载为类A的友元函数:可以重载为类A的友元函数,类A的对象就是运算符函数所要处理对象。
重载为类的成员函数:重载为类A的成员函数,则类A的对象和输入形参共同成为运算符所需处理对象。后置 单目运算符需要带一个整型形参,从而与前置单目运算符区别
让父类的指针指向子类对象时,如果父类和子类中有同名函数A,利用指针调用A时调用的是父类的函数A。虚函数是用来让指针调用A时调用的是子类的函数A
抽象类:建立抽象类,就是为了通过它多态地使用其中的成员函数,为一个类族提供统--的操作界面。通过抽象类的指针和引用可以指向并访问各派生类成员。
8.模板:逻辑功能相同而函数参数类型不同的多个重载函数用一个函数来描述
template<class class1,…… >:template中只对函数中使用的类进行抽象表示。可以有普通数据类型,此时相当于函数的形参。
函数调用形式:函数名<具体类型名1,具体类型名2,……,常量表达式> (实参表):尖括号中说明函数中使用的类具体指的是啥
9.STL(Standard Template Library):就是一些可扩展的类模板的集合,这些类叫做容器类
泛型:一个值取不同数据类型
STL主要由5个部分组成,分别是容器(container)、迭代器(iterator)、适配器(adaptor)、算法(algorithm)和函数对象(function object)。
9.1 容器
容器就是类模板,此类模板是用来生成不同数据类型下的数据结构,如生成int型栈、char型栈等。容器是个类肯定包含对数据结构的操作,如进栈、出栈、获取栈中元素个数等。
常见容器:
顺序容器
vector(向量):动态数组。从后端以push_back()添加元素。如果vector容器发现存储空间不足,整个容器中的数据元素将会被搬到一个新的、更大的内存空间中。并将所有数据元素复制到新的内存空间中。可通过[]访问元素(快速查找)。
容器valarray代表数学意义上的向量。
list(双向链表):每个元素均有指针指向前一个元素和下一个元素(快速 插入或删除)。list 容器中的元素可以分散存储在内存空间里,而不是必须存储在一整块连续的内存空间中。不可通过[]访问元素(快速查找)
deque(双端队列):与vector类似,这里的双端指的是可以从数组的两端插入数据。从前端或后端以push_front()或push_back()添加元素。在增加数据元素时,如果deque容器内原先定义的内存空间不足,deque容器会将新增加的数据元素存储到另外一块内存中。可通过[]访问元素(快速查找)
9.2 迭代器:一种通用的方法访问具有不同结构的各种容器中的每个元素,是广义的指针。每种容器定义自己的迭代器,如list
其他三种迭代器:reverse_iterator(用于反向遍历),const_iterator(返回一个常数值),const_reverse_iterator
9.3 适配器:可将顺序容器转为其他类型的容器。
三种适配器:队列(queue)、优先队列( priority queue)和栈(stack),
适配器也是类模板,但不是容器,无法使用迭代器。
1.直接将适配器当成类模板来用:
queue<int> q; // 创建一个q队列
q.push(1);
q.pop();
2.利用适配器将顺序容器转为其他类型的容器。
container adapter < typename T, container < typename T> > 变量名;
// container < typename T>表示待转化的顺序容器,两个typename T是一样的
// container < typename T>与右边的“>”之间要有一个空格,以免编译错误。
// 如将list转为queue:
queue<int, list<int> > list_q;
list_q.push(1);
list_q.pop();
关联容器:包括集合(set和multiset(允许有重复的元素))和键值对(map和multimap(允许同一个键有多个值))
STL算法:在
问题:
1.构造函数可以是私有的吗?
2.没有多继承,用虚继承会对结果产生影响吗?