🤖个人主页 :晚风相伴-CSDN博客
思维导图链接 :面向对象的性质
持续更新中......
💖如果觉得内容对你有帮助的话,还请给博主一键三连(点赞💜、收藏🧡、关注💚)吧
🙏如果内容有误或者有写的不好的地方的话,还望指出,谢谢!!!
让我们共同进步
目录
1、什么是面向对象
面向对象是一种编程思想,把一切东西都看成是一个个对象,比如人、耳机等,他们都有各自的属性
2、面向对象和面向过程的区别
面向过程:根据业务逻辑从上到下写代码
面向对象:将数据与函数绑定到一起,进行封装,这样能够更快速的开发程序,减少了重复代码的重写过程
3、面向对象的三大特性
封装 :将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互
继承 :可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展
多态:用父类指针指向子类的示例,然后通过父类指针调用实际子类的成员函数。实现多态
4、重载和重写的区别
重写 是指在派生类中重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致。只有函数体不同,派生类对象调用时会调用派生类的重写函数,不会调用被重写的函数。重写的基类中被重写的函数必须有virtual修饰
重载 是指同一作用域内被声明的几个具有不同参数列表的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型
5、重载和重写的实现
重载的实现:C++利用命名倾轧技术,来改变函数名,区分参数不同的同名函数。命名倾轧是在编译阶段完成的。
例如:void func(int a, int b)那么在编译的时候会把参数也加上,就变成了funcii
重写的实现:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。这就实现了一个多态
6、构造函数的种类和作用
默认构造、初始化构造、拷贝构造、移动构造
- 默认构造函数和初始化构造函数。在定义类的对象的时候,完成对象的初始化工作
- 赋值构造函数默认实现的是值拷贝(浅拷贝)
- 移动构造函数。用于将其他类型的变量,隐式转换为本类对象
7、向上转型和向下转型
子类转换为父类:向上转型,使用dynamic_cast<type_id>(expression) ,这种转换相对来说比较安全不会有数据的丢失
父类转换为子类:向下转型,可以使用强置转换,但这种转换是不安全的,会导致数据的丢失,原因是父类的指针或者引用的内存中可能不包含子类的成员的内存。
8、虚函数重写的条件
- 派生类可以不加virtual依旧满足重写,因为继承后父类的虚函数接口在子类中依旧保持虚函数属性
- 返回值是具有父子关系的指针或者引用(父类返回父类指针,子类返回子类指针)这个也叫做协变
- 析构函数的重写,如果父类的析构函数为虚函数,此时子类析构函数只要定义,无论是否加virtual关键字,都与父类析构函数构成重写。
9、深拷贝和浅拷贝
浅拷贝:又称值拷贝,将源对象的值拷贝到目标对象中去,本质上来说源对象和目标对象共用一份实体,只是所引用的变量名不同,地址其实还是相同的。
深拷贝:拷贝的时候先开辟出和源对象大小一样的空间,然后将源对象里的内容拷贝到目标对象中,这样两个指针就指向了不同的内存位置。
10、静态多态和动态多态
静态多态也称编译时多态或早期绑定多态。这种多态性通常是用重载实现,编译器根据调用时的参数类型和数量来决定使用哪个版本的函数
动态多态也称运行时多态,这种多态性通常通过虚函数来实现,允许子类重写父类的方法,并且能够在运行时根据对象的实际类型来选择合适的方法。
实现多态的条件
虚函数。基类中必须有虚函数,在派生类中必须重写虚函数。
通过基类类型的指针或引用来调用虚函数。
11、虚析构和虚构造
虚析构:将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。如果基类的析构函数不是虚函数,在特定的情况下会导致派生类无法被析构。
虚构造:虚函数对应一个虚表,这个表的地址是存储在对象的内存空间的。如果将构造函数设置为虚函数,就需要虚表中调用,可是对象还没有实例化,没有内存空间分配,调用不了(相违背)
12、类继承的访问权限
public继承:派生类可以访问基类的public、protected成员,不可以访问基类的private成员;派生类对象可以访问基类的public成员,不可以访问基类的protected、private成员。
protected继承:派生类可以访问基类的public、protected成员,不可以访问基类的private成员;派生类对象不可以访问基类的public、protected、private成员。
private继承:派生类可以访问基类的public、protected成员,不可以访问基类的private成员;;派生类对象不可以访问基类的public、protected、private成员。
13、虚函数和纯虚函数的区别
- 虚函数和纯虚函数可以定义在同一个类中,含有纯虚函数的类被称为抽象类,而只含有虚函数的类不能被称为抽象类
- 虚函数可以被直接使用,也可以被子类重载以后,以多态的形式调用,而纯虚函数必须在一个子类中实现该函数才可以使用,因为纯虚函数在基类有声明而没有定义。
- 虚函数和纯虚函数都可以在子类中被继承,以多态的形式被调用。
- 虚函数和纯虚函数通常存在于基类中,被子类所继承,目的是提供一个统一的接口。
- 虚函数的定义形式:virtual{};纯虚函数的定义形式:virtual{}=0;在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时要求前期绑定,然而虚函数却是动态绑定,而且被两者修饰的函数声明周期也不一样。
14、纯虚函数不能实例化的原因
纯虚函数在类的vftable中对应的表项被赋值为0。也就是指向一个不存在的函数。由于编译器绝对不允许有调用一个不存在的函数的可能,所以该类不能生成对象。在它的派生类中,除非重写此函数,否则也不能生成对象
15、拷贝构造函数的参数
- 拷贝构造函数的参数必须使用引用传递
- 如果使用传值的方式,那么会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函数。传指针也和传值一样。
16、拷贝赋值和移动赋值的区别
- 拷贝构造函数的形参是一个左值引用,而移动构造函数的形参是一个右值引用
- 拷贝构造函数完成的是整个对象或变量的拷贝,而移动构造函数时生成一个指针指向源对象或变量的地址,接管源对象的内存,相对于大量数据的拷贝节省时间和内存空间。
17、虚函数表
- 虚函数表是一个存储虚函数地址的数组,以NULL结尾。虚表在编译阶段生成,对象内存空间开辟后,写入对象中的虚表指针,然后调用构造函数。即虚表在构造函数之前写入。
- 除了在构造函数之前写入外,我们还需要考虑到虚表的二次写入机制,通过次机制让每个对象的虚表指针都能准确的指向到自己类的虚表,为实现动态多态提供支持。
- 虚表是在静态区的,并且虚表只有一份
对于派生类的虚表生成
- 先将基类的虚表内容拷贝一份到派生类虚表中
- 如果派生类重写了基类的某个虚函数,那么派生类自己的虚函数覆盖虚表中基类的虚函数
- 派生类自己增加的虚函数按其在派生类的声明顺序添加到派生类虚表的最后。
18、final和override
final用于修饰虚函数,表示该虚函数不能被重写
override用于检查子类虚函数是否重写了父类的某个虚函数,如果没有则编译报错
19、类模板和模板类的区别
类模板是模板的定义,不是一个实实在在的类,定义中用到通用类型参数
模板类是实实在在的类定义,是类模板的实例化,类定义中参数被实际类型所替代