后台开发核心技术与应用实践读书笔记(二)
第2章 面向对象的C++
2.1 类与对象
- 概念
- 成员函数
-
封装性
把数据和数据相关的操作封装在类里,只对可信的类或对象开放
- 构造函数
- 数据成员不能再类中初始化必须在构造函数中
- 类中可以定义不同的构造函数以提供不同的初始化方式(重载)
- 注意要在声明构造函数参数时指定默认参数值,而不是在定义中
- 如果定义了全是默认参数的构造函数,则不能定义重载构造函数
- 析构函数
- 函数中定义一个对象,函数调用完成时,对象会被释放
- static局部对象只在main与exit函数结束程序时,才会调用析构函数
- 全局对象与2相同
- new 建立的对象,delete时会调用
- 静态数据成员
- 可以把数据当成全局变量使用,但是又被隐藏在类的内部——使用静态数据成员
- 类的静态成员拥有一个存储区,多个对象共享这一块静态存储区,如果改变,则各个对象的这个数据成员的值都被改变了
- 静态数据成员类外初始化,并且不随类对象的建立与消失而改变
- 它在编译期间被分配空间,在程序结束释放
- 可以通过对象名来引用也可以通过类名来引用
- 静态成员函数
- 通过**类名::**和对象来调用
- 它的出现只是为了处理静态数据成员,直接引用该静态成员函数
- 静态成员函数没有 this指针,故不能访问本类的非静态成员
- 如果一定要引用该类的非静态成员函数,应加对象名,例如a.width;
- 对象的存储空间
- 空类占1个字节
- 静态数据成员、成员函数、构造/析构函数不占空间
- 如果一个类只有虚函数,则相当于含有一个指向虚函数的指针,64位8byte
- 函数代码是存储在对象空间之外的,而且函数代码共用
- this 指针
- 成员函数都包含的指针,指向被调用成员函数所在的对象
- 在成员函数开始前构造,在成员函数结束后清除,因此不能通过对象来使用this 指针
- 会应编译器的不同而有不同的存储位置,可能是栈、寄存器或全局变量区
- 普通类函数都不会创建一个函数表来保存函数指针,只有虚函数放到函数表中
- 类模板
- 多个类功能相同,数据类型不同
- 类外定义成员函数因写成:
template <class T>; T Operation<T>::add(){ return x+y;}
- 函数模板是编译时自动生 成各种类型的函数实例,如同内联函数,编译时其实现必须可见,故一般其实现都必须在头文件中
- 析构函数与构造函数的顺序
- 全局对象,构造函数在所有函数之前调用(包括main);不同文件都有全局对象,则构造顺序不可知。并且在main结束或exit调用析构函数
- 局部对象, 局部构造与析构,多次调用多次构造析构
- 函数中有静态局部对象,只在程序第一次调用该函数时构造,结束也不析构,只有在main或者exit函数结束析构
2.2 继承与派生
-
继承性
可以让某个类型的对象获得另一个类型的对象的属性的方法。即子类可以获得父类的属性和方法,实现了代码重用
- 派生类的访问属性
- 公有继承,基类的共用成员和保护成员保持原有属性,私有成员基类私有
- 保护继承,基类的共用成员和保护成员在子类中变成了保护成员,类外不可见,私有成员基类私有
- 私有继承,共有成员与保护成员在子类中变成私有成员,类内使用,私有成员同上面1、2
-
派生类的构造函数与析构函数
对派生类的构造函数有以下说明:
- 基类成员与**子对象成员(指的是数据成员为一个类对象)**必须初始化列表中初始化
- 派生类的构造顺序:基类构造函数->子对象构造函数->派生类构造函数体
- 有多个基类时;调用顺序按照定义派生类时生命的顺序(自左向右),与初始化列表无关
- 如果派生类的基类也是派生的,只需负责直接基类的构造,依次往上
- 派生类有多个子对象成员,派生时按照声明的顺序(从前往后)
- 基类的构造函数定义了一个与多个参数时,派生类必须定义构造函数
- 基类中定义了默认构造函数或没有定义构造函数,派生类可以省略对基类构造函数的调用(<基类名>(<参数表>)),子对象成员也相同
- 所有基类或子对象的构造函数都可以省略时,可以省略基类构造函数的调用
- 基类与子对象构造函数都不要参数,派生类参数也不需要时,派生类构造函数可以不定义
- 不继承基类的析构函数,但是需要通过派生类的析构函数去调用基类的析构函数(系统来完成的)
- 可根据自己的需要定义自己的析构函数对增加成员进行清理
- 在执行派生类的析构函数时,系统会自动调用基类和子对象的析构函数;
- 派生类的构造函数与析构函数的额调用顺序
- 构造函数的调用顺序上一小节以给出
- 析构函数的调用顺序与构造函数正好相反
- 析构函数在下列3种情况下被调用
- 对象生命周期结束时
- delete该对象的指针,或指向对象基类类型的指针(其基类析构函数必须是虚函数),这里就是为什么基类的析构函数设定为虚函数的原因;
- 对象i是o的成员,O被析构,i也会被析构
2.3 类的多态
-
多态
多态性是指具有不同功能的函数可以使用一个函数名,在面向对象方法中:向不同的对象发送同一个消息,不同的对象在接受时会有不同的行为(即执行不同的函数)。主要体现在子类对父类函数的重写
注意:
- 派生类重新定义此函数,要求函数名、函数类型、参数个数与类型全部与基类虚函数一致
- 定义一个指向基类对象的指针,并将它指向同一类族的对象,由此可以调用相应类中的函数
- 非虚函数在子类重新定义,则基类指针调用此函数则调用的是基类的成员函数,若是派生类指针则调用派生类的函数,这不是多态行为。
-
虚函数的使用
使用虚函数时,系统具有一定的空间开销。当一个类带有虚函数时,编译系统会为之分配一个虚指针。系统在动态关联时的时间开销是很少的。因此多态是高效的
- 纯虚函数
- 基类没有定义,原型后加“=0”,这是因为基类本身生成的对象是不合理的,比如动物这个基类
- 子类一定要实现,否则编译出错
- 析构函数
- 构造函数不能声明为虚函数原因
- 编译器构造对象在构造时必须知道确切类型
- 构造之前,对象不存在,无法使用指向此对象的指针来调用构造函数
-
析构函数声明为虚函数原因
C++明确指出**:子类对象由父类指针删除,而基类即析构函数是非虚的,会导致对象的子类成分没有析构掉,这样容易引发内存泄漏**
- 构造函数不能声明为虚函数原因
-
单例模式
单例模式的要点有三个:
- 单例类有且仅有一个实例
- 单例类必须自行创建自己的唯一实例
- 单例类必须给所有其他对象提供这一实例
线程安全单例模式class CSingleton { private: CSingleton() //构造函数是私有的 { } static CSingleton *m_pInstance; public: static CSingleton * GetInstance() { if(m_pInstance == NULL) //判断是否第一次调用 m_pInstance = new CSingleton(); return m_pInstance; } };
// 线程安全的单例模式 class Singleton { private: Singleton() { } ~Singleton() { }cpp Singleton(const Singleton &); Singleton & operator = (const Singleton &); public: static Singleton & GetInstance() { static Singleton instance; return instance; } };
索引
- 第 1 章 C++常用的编程技术
- 第 2 章 面向对象C++
- 第 3 章 常用的STL使用
- 第 4 章 编译
- 第 5 章 调试
- 第 6 章 TCP协议
- 第 7 章 网络IO模型
- 第 8 章 网络分析工具
- 第 9 章 多线程
- 第 10 章 进程
- 第 11 章 进程间通信
- 第 12 章 HTTP协议