1、const
1.1 指针常量和常量指针
说说const int *a, int const *a, const int a, int *const a, const int *const a分别是什么,有什么特点。
const int *a==int const *a; //可以通过 a 访问整数值,但不能通过 a 修改该整数的值,指针本身是可变的,可以指向不同的整数
const int a; //a变量变成常量,不可修改
int *const a; //a的值可以更改,但是指向它的指针不能更改
int const *const a; //a本身和指向它的指针都不能更改
1.2 const成员函数
常函数内不能修改成员变量
对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。
1.3 const和#define的区别
1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。
而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误(边际效应) 。
(2)有些集成化的调试工具可以对 const 常量进行调试,但是不能对宏常量进行调试。
(3)#define是在编译的预处理阶段起作用,而const是在编译、运行的时候起作用。
(4)#define定义的常量不分配内存,而const定义的常量会分配在常量存储区中。
具体可看以下博客7.5小节
C++入门基础(二)_c语言最小体重-CSDN博客文章浏览阅读387次。初学C++,每周更新自己所学!_c语言最小体重https://blog.csdn.net/qq_56896418/article/details/127675418?spm=1001.2014.3001.5502
2、 虚函数
2.1 作用
父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。
2.2 实现
每个虚函数都会有一个与之对应的虚函数表,该虚函数表的实质是一个指针数组,存放的是每一个对象的虚函数入口地址。对于一个子类来说,他会继承父类的虚函数表同时增加自己的虚函数入口地址,如果子类重写了基类的虚函数的话,那么继承过来的虚函数入口地址将被子类的重写虚函数入口地址替代。那么在程序运行时会发生动态绑定,将父类指针绑定到实例化的对象实现多态。每个类只有一个虚函数表,虚函数表是在编译的时候就确定的了。
2.3 纯虚函数
用户不能创建基类的实例,只能创建派生类的实例
2.4 虚函数调用时机
虚函数在运行时根据实际对象的类型来确定调用哪个函数,而不是根据指针或引用的类型来确定。当一个虚函数被定义为类的成员函数时,它会被标记为虚函数。在调用虚函数时,程序会查找该函数的实际类型,并在运行时调用该类型的实现。这就允许程序在运行时动态地选择执行哪个版本的虚函数,从而实现多态性。虚函数通常与父类指针或引用一起使用,可以实现父类指针或引用调用子类的函数。
2.5 大小
虚函数表是一个存储虚函数指针的数组,每个类有一个虚函数表,每个对象有一个指向虚函数表的指针。虚函数表的大小取决于类中有多少个虚函数,而对象中的虚函数表指针的大小取决于编译器和操作系统。一般来说,在32位系统下,指针占4个字节,在64位系统下,指针占8个字节。
2.6 C++哪些函数不能定义为虚函数
构造函数:构造函数不能被声明为虚函数。因为构造函数是用来创建对象的,而虚函数是根据对象的类型来动态调用的。如果构造函数是虚函数,那么在创建对象时就无法确定调用哪个版本的构造函数,会导致逻辑错误
友元函数:友元函数实际上并不属于类的成员函数,所以不能被定义为虚函数
普通函数:普通函数只能被重载,不能被重写。
2.7 为什么虚函数不能是模版函数
因为模板函数在编译时会被实例化为多个不同的函数,而虚函数需要在运行时才能确定调用哪个函数。在C++中,虚函数的实现依赖于虚函数表(vtable)和虚函数指针(vptr),而这些在编译时就需要确定下来。因此,虚函数不能是模板函数。
2.8 虚函数表既然希望类的所有对象共享为什么不放在全局区
虚函数表不能放在全局区,因为全局区是存放全局变量和静态变量的,而虚函数表不是变量,而是一组指向类成员函数的指针。如果放在全局区,会导致内存浪费和混乱。
混乱:虚函数表是在编译期就确定了大小和内容的,而全局区是在运行期才分配空间的。如果把虚函数表放在全局区,就需要在运行期动态地为每个类分配空间,并且要保证不同类之间不会发生冲突。这样就增加了程序的复杂度和出错的可能性。
C++核心编程-4、类和对象4—多态-CSDN博客文章浏览阅读287次。【代码】C++核心编程-4、类和对象4—多态。https://blog.csdn.net/qq_56896418/article/details/140632373?spm=1001.2014.3001.5502
3、菱形继承
菱形继承(Diamond Inheritance)是一种多重继承的情况,其中一个子类同时继承自两个直接或间接共同父类,而这两个父类又继承自同一个共同的父类。这样就形成了一种菱形的继承结构,因此称为"菱形继承"。
解决方法:虚继承,出现二义性通过添加作用域解决,而继承爷爷类的数据有两份,通过虚继承解决。
具体看博主下面这篇博客的菱形继承部分。
C++核心编程-4、类和对象3—继承-CSDN博客文章浏览阅读237次,点赞9次,收藏3次。在Son3中已经变为了私有,即使孙子类公共继承,依然不不能访问,仍是私有权限。然后我们采用VS自带的工具进行验证:Vs的开发人员命令提示符。https://blog.csdn.net/qq_56896418/article/details/140590494?spm=1001.2014.3001.5502
4、类型转换
4.1 static_cast
static_cast 用于执行非多态类型之间的类型转换,例如整型和浮点型之间的转换、基类和派生类之间的指针或引用转换、void 指针和其他指针类型之间的转换等。该转换在编译时完成,通常不会检查运行时错误。
4.2 dynamic_cast
dynamic_cast 用于在运行时进行多态类型的转换。它通常用于将基类指针或引用转换为派生类指针或引用,以及在类层次结构中进行下行转换
4.3 dynamic_cast与虚函数的区别
功能:虚函数: 实现运行时多态性,使得可以在基类指针或引用中调用派生类的方法。
dynamic_cast: 用于类型安全的向下转型或交叉转型,确保转换成功与否。
实现方式:虚函数: 通过虚表(vtable)机制实现动态绑定。
dynamic_cast: 依赖于运行时类型识别(RTTI)来安全地转换类型。
使用场景:虚函数: 用于实现和利用多态性。
dynamic_cast: 用于在复杂的类层次结构中进行安全的类型转换。
4.4 reinterpret_cast
reinterpret_cast 用于在不同的指针类型之间进行转换,**例如将一个指针转换为一个整数,或将一个整数转换为一个指针。**该转换通常不进行类型检查,因此潜在地不安全,只应在极少数特殊情况下使用。
4.5 const_cast
const_cast 用于在去除变量的 const 修饰符或 volatile 修饰符时使用。它可以将指向常量对象的指针或引用转换为指向非常量对象的指针或引用,或者将指向非常量对象的指针或引用转换为指向常量对象的指针或引用。
4.6 volatile关键字
在 C++ 中,关键字 volatile 用于声明一个变量是易变的(volatile variable),即该变量可能会在程序中的任意时刻被意外地改变。这意味着,当读取一个易变的变量时,编译器不会从缓存中读取该变量的值,而是每次都会从内存中重新读取该变量的值。同样地,当写入一个易变的变量时,编译器也不会将该变量的值存储在缓存中,而是立即将该变量的值写入内存中。
5、构造函数
具体可看博主这篇博客的4.2小节。有代码和更详细的分析。
C++核心编程-4、类和对象1-CSDN博客文章浏览阅读375次,点赞12次,收藏3次。分析:类中包含属性和行为,属性为变量r,行为为函数calculate。而对象是通过类创建的实例化对象,具体化出一个对象。https://blog.csdn.net/qq_56896418/article/details/140463895?spm=1001.2014.3001.5502
5.1 拷贝构造函数
什么时候调用拷贝构造函数(上述博客中的4.2.3中的三种)
答:用已经初始化的对象给另一个初始化的对象赋值。
函数用对象作为返回值。
函数用对象作为参数。
5.2 析构函数
析构函数为什么要声明为虚的?
A* p = new B;
delete p;(A是父类 B是子类)
如果不定义虚析构 那么删除P只调用A的析构,而不会调用子类的析构函数。
定义为虚析构之后,删除P就会调用AB的析构
5.3 移动构造函数
在C++中,当一个对象被复制时,其内部资源通常会被复制,这可能会导致性能问题,特别是在处理大型数据结构时。移动构造函数是当你将一个大对象赋值给另一个对象时,移动构造函数会将资源所有权转移,而不是复制数据。
6、引用
注意事项(引用即起一个别名)
引用必须初始化
引用在初始化后,不可以改变
6.1 C++类内是否可以定义引用?
可以,但是必须使用成员初始化列表为引用变量初始化,构造函数的形参也必须是引用类型
在 C++ 中,类内可以定义引用,但有一些特殊的规则和注意事项。
总结
引用成员必须在初始化列表中初始化,不能在类体中单独赋值。
引用必须绑定到有效对象,在引用的整个生命周期内该对象必须存在。
常量引用 也可以在类中使用,并且一旦绑定后无法修改。
7、模版类
C++提高编程—1、模板-CSDN博客文章浏览阅读273次,点赞4次,收藏3次。示例:两种调用模版的方式。https://blog.csdn.net/qq_56896418/article/details/140750118?spm=1001.2014.3001.5502
7.1 模板类是什么时候实现的?
模板类的实现不是在程序运行时期间动态生成的,而是在编译阶段根据需要进行实例化和生成对应的代码。这也是为什么在使用模板类时,模板类的声明和定义通常需要放在头文件中,以便编译器在需要的地方进行实例化,并生成相应的代码。
7.2 模板的实例化
在 C++ 中,模板的实例化是指创建特定类型或特定参数的模板实例的过程。这使得模板能够处理具体的数据类型或值,并生成适用于这些类型或值的具体代码。实例化是模板的一个重要概念,它确保模板代码被转换为实际可执行的代码。