第二部分 面向对象程序设计
类
1、在类中定义的成员函数一般规模都比较小,语句只有1~5句,而且特别的swtich语句不允许使用。他们一般为内联函数,即使没有明确用inline表示。
2、在C++中,函数声明在头文件中,但函数定义不能在头文件中,因为他们将被编译多次。3、如果是内联函数,包含在头文件中是允许的,因为内联函数在原程序中原地扩展。由于在类中定义的成员函数被默认为内联函数,所以就避免了不能被包含在头文件中的问题。
3、C++对所有类的成员函数都隐藏了第一个参数this。
堆
1、C++程序的内存格局通常分为四个区:
(1) 全局数据区(data area):全局变量,静态数据,常量
(2) 代码区(code area):所有类成员函数和非成员函数代码
(3) 栈区(stack area):为运行函数分配的局部变量,函数参数,返回数据,返回地址等
(4) 堆区(heap area):余下的所有空间
3、 从堆上分配对象数组,new的格式是类型后面跟[元素个数],不能再跟构造函数参数,所以只能调用默认的构造函数,不能调用其他任何构造函数。
拷贝构造函数
1、用一个对象对构造另一个对象,或者说用一个对象值初始化另一个新构造的对象。
2、对象作为函数参数传递的时候,也要涉及对象的拷贝。
3、拷贝构造函数应该是Student(Student& s)
默认拷贝构造函数
1、类定义中,如果未提供自己的拷贝构造函数,则C++提供一个默认拷贝构在函数,就像没有提供构造函数时,C++提供默认构造函数一样。
2、默认拷贝构造函数工作的方式是:完全拷贝一个成员的内容。
浅拷贝和深拷贝
1、 默认拷贝构造函数中,拷贝的策略是逐个成员依次拷贝。
2、 如果一个对象的构造函数为其分配了一个资源,则新的对象也会拥有这个资源,即两个对象拥有同一个资源。当对象析构的时候,该资源将经历两次资源返还。这称为浅拷贝。
3、 如果你的泪需要析构函数来析构资源的话,则它就需要一个拷贝构造函数。
静态成员
1、 在类的对象空间中,是没有静态数据成员的,他的空间不会随着对象的产生而分配,或随着对象的消失而回收。所以他的空间分配并不在类的构造函数里完成,并且空间回收也不再析构函数里完成。
2、 类声明纸声明一个类的尺寸与规格,并不进行实际的内存分配,所以在类声明中写成定义static int noOfStudents = 0 是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。
3、 在类的外部,访问静态数据成员的形式可以是s1.noOfStudents,它等价于s2.noOfStudents,更通常的用法是Student::noOfStudents (不能用Student.noOfStudents)。
静态数据成员懂得比较多的场合一般为:
1、 用来保存流动变化的对象个数(noOfStudents)
2、 作为一个标志,指示一个特定的动作是否发生
3、 一个指向一个链表第一成员或最后一个成员的指针(如pFirst)
静态成员函数
1、与静态数据成员一样,静态成员函数与类相联系,不与类的对象相联系,所以访问静态成员对象时不需要对象。
2、一个静态成员函数不与任何对象相联系,故不能对非静态成员进行默认访问。因为程序不知道访问的是哪个对象的非静态成员。
4、 静态成员函数没有this指针,而非静态成员函数有一个指向当前对象的指针this。
友元
需要友元的原因:
1、 普通函数需要直接访问类的成员或私有数据成员主要是为了提高效率。
2、 为了方便重载操作符的使用
友元的使用
1、 在类里声明一个普通函数,标上关键字friend,就成了该类的友元,可以访问该类的一切成员。
friend Vector Multiply(Matrix& m, Vector& v);
2、 友元声明的位置可在类的任何部位,既可在public区,也可在protected区,意义完全一样。
3、 友元函数定义在类的外部,一般与类的成员函数定义放在一起。因为类重用时,一般友元是一起提供的。
4、 一个类的成员函数可以是另一个类的友元。
friend void Teacher::assignGrades(Student& s);
5、 整个类可以是另一个类的友元
friend class Teacher;
继承
1、如果没有声明派生类的构造函数,则执行默认构造函数。默认构造函数会首先调用基类的默认构造函数or正好匹配参数的构造函数。
2、派生类可以直接访问基类的保护数据成员,甚至在构造时初始化它们。但是一般不这么做,而是通过基类的接口去访问它们,初始化也是通过基类的构造函数。这样做的好处是一旦基类的实现有错误,只要不涉及接口,那么基类的修改不会影响派生类的操作。
类与类之间,你作你的,我做我的,以接口沟通。即使基类和子类也不例外。
Class GraduateStudent : public Student
{
public:
GraduateStudent(char * pName, Advisor&[11051] adv) : Student(pName), Advisor(adv)
{
…
}
}
3、 派生类可以在参数为基类的函数中做参数,如GraduateStudent类的对象可以用于参数类型为Student的函数
4、 派生类可以继承基类的所有共有和保护的数据成员和成员函数。
多态(迟后联编late binding)
1、 为了指明某个成员函数具有多态性,用关键字virtual来标志其为虚函数。
2、 基类中虚函数的性质会自动的带给其子类,所以子类中virtual可以省略
3、 如果虚函数在基类与子类中出现的仅仅是名字的相同,而参数类型不同,或返回类型不同,即使写上了virtual关键字,也不进行迟后联编。
虚函数的限制:
1、 只有类的成员才能说明为虚函数
2、 静态成员函数,内联函数,构造函数不能是虚函数
3、 析构函数可以是虚函数,而且通常声明为虚函数
减少类的冗余
继承给程序员可在一个过程中从不同类里组合共有特征的能力,这个过程称为分解(factoring)。也就是把共同的特征分解到基类中。
从相似的类中,将共有特征提取出来的过程成为分解。分解使类的层次合理化和减少冗余。只有当继承关系与实际相符和的时候,分解才是合理的。
一个程序员努力工作,写出比较巧妙的代码,以达到减少一些程序行的目的,是不值得的。这种巧妙常常会弄巧成拙。但是通过继承分解出多余部分,可以合理的减少编辑的工作量。
抽象类、具体类、纯虚函数
1、 抽象类是不能有实例对象的类,因为实际上不存在这样的对象。这样的类唯一的用途是被继承。
2、 抽象类是作为基类为其他类服务的。
3、 不能创造一个抽象类的对象,但是可以声明一个抽象类的指针或引用,供成员函数使用。
4、 直到所有纯虚函数被重载之前,抽象类的子类也一直保持抽象状态。
5、 一个抽象类至少具有一个纯虚函数。纯虚函数是指被标明为不具体实现的虚成员函数,即不知道怎么实现,但又不得不给它定义的函数。
6、 纯虚函数是在基类中为子类保留的一个位置,以便子类用自己的实在函数定义来重载之。如果在基类中没有保留位置,则就没有重载。
virtual void Withdrawal(float amount) = 0 //纯虚函数,=0表明程序员将不定义该函数,该声明是为派生类而保留的位置
多重继承
要解决的问题:
继承的模糊性:多个基类里有相同的成员时,无法分辨派生类继承的是拿一个基类的成员。
解决方法:
虚拟继承(Virtual inheritance)
class Sofa:virtual public Furniture {…}
class Bed:virtual public Furniture (…)
class SleeperSofa:public Sofa, public Bed {…}
多继承的构造顺序:
(1) 虚拟基类的构造函数按照继承顺序构造
(2) 非虚拟基类的构造函数按照继承顺序构造
(3) 成员对象按照声明顺序构造
(4) 类自己的构造函数
继承的访问控制
继承类型 | Public | Protected | Private |
Public | Public | Protected | Private |
Protected | Protected | Protected |
|
private | Private | Private |
|
(1) 在单个类中(无继承的情况下),protected和private没有什么区别
(2) 如果不标明继承为公共还是保护或者私有,则默认的继承是私有。
(3) 在面向对象程序设计中,继承和多重继承一般指公共继承,保护继承和私有继承只在技术上讨论时有一席之地。保护继承和私有继承类似,继承之后的类相对于基类而言是独立的,在公开场合不能使用基类的成员。