- 1、多态性
- 2、运算符重载
- 注意:
- 注意:
- 3、虚函数
- 实现动态多态的必要条件:
- 注意:
- 重载、重写、重定义的区别:
- 4、纯虚函数和抽象类
- 5、虚析构函数和纯虚析构函数
- 何时会用虚析构函数?
- 何时会用到纯虚析构函数?
- 如何计算一个类对象的大小:
- 6、模板
- 1、函数模板
- 2、类模板
多态性是指向不同的对象发送同一消息会产生不同的行为
举例:
按q 盲僧:天音波 压缩:斩钢闪
盲僧->q(); 压缩->q();
(1)运行期多态:动态多态,早绑定:虚函数
(2)编译器多态:静态多态,晚绑定:函数重载,运算符重载,模板
2、运算符重载C++中的运算符只能对基本数据类型进行运算,无法对类对象进行运算
实现机制:
将指定的运算符表达式转换为运算符函数,运算对象转换为函数的参数
A a,b;
a + b ----> operator+(a,b) ---->调用—>a + b
注意:(1)只能重载C++中已有的运算符,不能臆造新的
(2)不能改变运算符原有的优先级和结合性
(3)不能改变操作数个数
(4)经重载后的运算符,其操作数至少有一个是自定义类型
(5)可以重载除以下运算符以外的所有运算符:
. .* :: ?: sizeof
重载方式:
(1)重载为类成员函数
参数个数 = 原操作数个数 - 1(思考:为什么-1?因为this指针),除后置+±-外
(2)重载为友元函数
参数个数 = 原操作数个数
注意:有些运算符只能重载为友元函数,有些运算符只能重载为类成员函数,有些运算符两种方式都可以。
= -> () [] 这四个运算符只能重载为类成员函数,不能重载为友元函数。
声明方式:
返回类型 operator运算符(参数);
如: operator+
C++中临时变量不能作为非const的引用参数
#include3、虚函数using namespace std; void f(int &a) //非const引用 { cout << "f(" << a << ") is being called" << endl; } void g(const int &a) //const引用 { cout << "g(" << a << ") is being called" << endl; } int main() { int a = 3, b = 4; f(a + b); //编译错误,把临时变量作为非const的引用参数传递了 g(a + b); //OK,把临时变量作为const&传递是允许的 }
概念:在函数原型前加上virtual关键字,就变成了虚函数,如:virtual void fun();
作用:用来实现动态多态
实现原理:当基类指针或引用指向派生类对象时,如果是普通函数,则调用的是基类中的函数,如果是虚函数,则调用的是派生类中的函数
虚函数会在内存中生成虚函数表vtable,一个虚函数表对应一个虚表指针vptr
基类和派生类公用同一个虚函数表
实现动态多态的必要条件:1、要有继承
2、要有虚函数的重写(覆盖) //重载、重写(覆盖)、重定义(隐藏)的区别?
3、基类指针或引用指向派生类对象
注意:在派生类中不写virtual关键字,依然是虚函数
重载、重写、重定义的区别:重载:同一作用域内有相同函数名,不同参数列表的函数(同一个类中)
重写(复写、覆盖):在派生类中,重新定义从基类继承而来的虚函数,并且函数名,参数,返回值必须一致(在基类和派生类中)
重定义(隐藏):在派生类中,重新定义从基类继承而来的函数,函数名相同,参数和返回值不同(在基类和派生类中),只要不构成重写就是重定义。
4、纯虚函数和抽象类概念:在虚函数后面加上“=0”,就实现了纯虚函数,如:virtual void fun() = 0;
作用:在许多情况下,在基类不能对虚函数作出有意义的实现,那么就可以将这个虚函数设为纯虚函数,这样在基类中不用给出实现,实现就留给派生类自己,但是后面依然可以给出纯虚函数的实现。
抽象类:包含纯虚函数的基类叫做抽象类。抽象类不能实例化(创建对象),只为派生类提供接口,如果派生类继承了纯虚函数,依然没有给出实现,那么这个派生类还是抽象类。
5、虚析构函数和纯虚析构函数 何时会用虚析构函数?当用基类指针指向派生类对象时,要通过基类指针来删除派生类对象时,那么要将基类的析构函数设为虚函数,否则调用基类的析构函数,不会调用派生类的析构函数,造成内存泄漏。但是不要无故的将析构函数设为虚函数,因为虚函数会占用内存
何时会用到纯虚析构函数?当需要把一个基类设为抽象类时,需要引入纯虚函数,但此时又 没有 有意义的纯虚函数,那么就可以将析构函数设为纯虚析构函数。
为什么构造函数不能为虚函数?
虚函数对应虚函数表vtable,虚函数表是存在在对象的内存空间的,构造函数是在创建对象由系统自动调用,如果构造函数是虚函数,就在存在虚函数表中,但是此时对象还没分配空间,没有对应的虚函数表,所以构造函数不能为虚函数。
如何计算一个类对象的大小:1、空类占一个字节
2、类中的函数(普通函数、静态函数、虚函数)都不占空间
3、虚函数本身不占空间,但是虚表指针占空间,所有虚函数公用一个虚表指针
4、普通成员数据占空间,静态成员数据不占空间
一个类的大小 >= 所有非静态成员数据大小的总和
5、有继承时,基类和派生类公用一个虚表指针
6、计算类对象大小时,也会按照字节对齐
7、多继承时,有几个基类就有几个虚表指针
8、虚继承时,会生成虚基表,对应的虚基表指针
一个类的大小 = 所有非静态成员数据大小的总和 + 虚表指针(多继承时有多个)+虚基表指针(多继承有多个)+对齐的字节数。
6、模板 1、函数模板函数的重载可以实现一个函数名重用,但还是得写多个函数,用不同的参数实现多态。函数模板可以只写一个函数,就能实现多态。
声明方式:
template 或者 template
class和typename 可以相互互换
struct不能实现模板
template2、类模板//template T compare(T a,T b) { return a>b?a:b; } int main(void) { int a1 = 10,b1 = 20; float a2 = 10,b2 = 20; double a3 = 10,b3 = 20; cout << compare(a1,b1) << endl; cout << compare(a2,b2) << endl; cout << compare(a3,b3) << endl; }
#include#include using namespace std; template class A { public: A(T1 a,T2 b) { this->a = a; this->b = b; } void show(); private: T1 a; T2 b; }; template void A ::show() { cout << a << endl; cout << b << endl; } int main(void) { //A a(1,1.2); A a1(1,2); A a2(1.2,"123"); A a3("abc","123"); a1.show(); a2.show(); a3.show(); }



