复习文档2

C++中的四种cast转换 C++四种类型的转换时:static_cast,dynamic_cast,const_cast,reinterpret_cast

1、const_cast
使用场景:
A.常量指针转换为非常量指针,并且仍然指向原来的对象。
B.常量引用被转换为非常量引用,并且仍然指向原来的对象。
使用特点:
A.const_cast是唯一可以对常量进行操作的转换符
B.取出常量性是一个危险的动作,尽量避免使用。一个特定的场景是:类通过const提 供重载时,一般都是非常量函数调用const_cast将参数转换为常量,然后调用 常量函数,然后得到结果再调用const_cast去除常量。

2、static_cast
A.用于类层次结构中基类与派生类之间指针或引用的转换
上行转换(派生类—>基类)是安全的
下行转换(基类—>派生类)由于没有动态类型检查,所以是不安全的
B.用于基本数据类型之间的转换,如把int转换为char,这种带来安全性问题由程序员 来保证。
C.把空指针转换成目标类型的空指针
D.把任何类型的表达式转为void类型
使用特点
1.主要执行非多态的转换操作,用于代替C中通常的转换操作
2.隐式转换都建议用static_cast进行表明和替换

3、dynamic_cast
dynamic_castexpression;type-id必须是指针,上述格式一样但是指针没有要求
使用场景:
只有在派生类之间转换时才使用dynamic_cast,type_id必须是类指针,类引用或者void*。
使用特点:
1.基类必须要有虚函数,因为dynamic_cast是运行时检查,需要运行时类型信息,而这个信息存储在类的虚函数表中,只有一个类定义了虚函数,才会有虚函数表(如果一个类没有虚函数,那么一般意义上,这个类的设计者也不希望它成为一个基类)。
2.对于下行转换,此转换是安全的(当类型不一样时,转换过来是空指针),而static_cast是不安全的(当类型不一致时,转换过来的是错误意义的指针,可能造成踩内存,非法访问等各种问题)
3.此转换可以进行交叉转换
用于动态类型的转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。向下转化时,如果是非法的对于指针返回NULL,对于引用抛异常。
向上转换:指的是子类向基类的转换。
向下转换:指的是基类向子类的转换
它通过判断在执行到该语句的时候变量的运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。
4、reinterpret_cast

几乎什么都可以转,比如int转指针,可能会出问题,尽量少用。
使用场景:
不到万不得已,不能用,高危操作
使用特点:
1.从底层对数据进行重新解释,依赖具体的平台,可移植性差
2.可以将整型转换为指针,也可以把指针转换为数组
3.可以在指针和引用里进行肆无忌惮的转换
各类型之间的区别
在基类和派生类互相转换时,虽然static_cast是在编译期完成,效率更高,但是不安全,相比之下,因为dynamic_cast可以查看运行时信息,从而保证数据的安全性
Static_cast虽然不是一种绝对安全的转换,但是它在转换时,还会进行必要的检测(例如指针的越界,类型检查)。Reinterpret_cast完全是肆无忌惮,直接从二进制开始重新映射解释,极度不安全。

5、为什么不使用C的强制转换

C的强制转换表面上看起来功能强大什么都能转,但是转化不够明确,不能进行错误检查,容易出错。
野指针
野指针就是指向一个已删除的对象或者未申请访问受限内存区域的指针。

为什么析构函数要是虚函数?为什么C++默认的析构函数不是虚函数

将可能会被继承的父类的析构函数设置为虚函数,可以保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。
C++默认的析构函数不是虚函数是因为虚函数需要额外的虚函数表和虚表指针,占用额外内存。而对于不会被继承的类来说,其析构函数如果是虚函数,就会浪费内存。因此C++默认的析构函数不是虚函数,而是只有当需要父类时,设置为虚函数。

函数指针

1、函数指针是指向函数的指针。
函数指针首先是一个指针变量,该指针变量指向一个具体的函数。
C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。
2、调用函数和做函数的参数。(回调函数)

请你介绍一下fork函数

Fork:创建一个和当前进程映像一样的进程可以通过fork()系统调用:
#include<sys/types.h>
#include<unistd.h>
pid_t fork(void);
成功调用fork()会创建一个新的进程,它几乎与调用fork()的进程一模一样,这两个进程都会继续进行。在子进程中,成功的fork()调用会返回0。在父进程中fork()返回子进程pid。如果出现错误,fork()返回一个负值。

最常见的fork()用法是创建一个新的进程,然后使用exec()载入二进制映像,替换当前进程的映像。这种情况下,派生(fork)了新的进程。而这个子进程会执行一个新的二进制可执行文件的映像。这种“派生加执行”的方式是很常见的。
在早期的unix系统中,创建进程比较原始。当调用fork时,内核会把所有的内部数据结构复制一份,复制进程的页表项,然后把父进程的地址空间中的内容逐页的复制到子进程的地址空间中。但从内核角度来说,逐页的复制方式是十分耗时的。现代的unix系统采取了更多的优化,例如linux,采用了写时复制的方法,而不是对父进程空间进程整体复制。

析构函数

1、析构函数与构造函数对应,当对象结束其生命周期,如对象所在的函数已调用完毕时,系统会自动执行析构函数。
2、析构函数名也应与类名相同,只是在函数名前面加一个~,它不能带任何参数,也没有返回值(包括void类型)。只能有一个析构函数,不能重载。
3、如果用户没有编写析构函数,编译系统会自动生成一个缺省的析构函数(即使自定义了析构函数,编译器也总是会为我们合成一个析构函数,并且如果自定义了析构函数,编译器在执行时会先调用自定义的析构函数再调用合成的析构函数),它也不进行任何操作。所以许多简单的类中没有显式的析构函数。
4、如果一个类中有指针,且在使用的过程中动态地申请了内存,那么最好在析构函数销毁类之前,释放掉申请的内存空间,避免内存泄漏。
类析构的顺序:1)派生类本身的析构函数;2)对象成员析构函数;3)基类析构函数;

C++中类成员的访问权限

C++通过public、protected、private来控制成员变量和成员函数的访问权限,在类的内部,无论成员被声明为哪种,都是可以互相访问的,没有访问权限的限制。在类的外部,只有通过对象访问成员,并且通过对象只能访问public属性的成员,不能访问protected、private属性的成员。

Struct和class的区别
在C++中,可以用struct和class定义类,都可以继承。
区别在于:struct的默认继承权限和默认访问权限是public,而class的默认继承权限和默认访问权限是private。另外,class还可以定义模板类形参,比如template<class T,int i>。

什么是右值引用,跟左值有什么区别?
右值引用实现了转移语义和精确传递。
它的目的有两个方面:
1、消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
2、能够更简洁明确地定义泛型函数。

左值和右值得概念:

左值:能对表达式取地址、或具名对象/变量。一般指表达式结束后依然存在的持久对象。
右值:不能对表达式取地址,或匿名对象。一般指表达式结束就不再存在的临时对象。
右值引用和左值引用的区别:
1、左值可以寻址,而右值不可以。
2、左值可以被赋值,右值不可以被赋值,可以用来给左值赋值。
3、左值可变,右值不可变。

C++源文件从文本到可执行文件经历的过程

1)预处理;2)编译;3)汇编;4)链接

头文件双引号和尖括号之间的区别
1、双引号
1)当前头文件目录2)编译器设置的头文件路径3)系统变量()指定的头文件路径
2、尖括号
1)编译器设置的头文件路径2)系统变量指定的头文件路径

C++11最常用的新特性

1)auto关键字:编译器可以根据初始值自动推导出类型,但是不能用于函数传参以及数组类型的传参。
2)nullptr关键字:nullptr是一种特殊类型的字面值,它可以被转换成任意其他的指针类型;而NULL一般被宏定义为0,在遇到重载时可能会出问题。
3)智能指针:C++11新增了std::shared_ptr、std::weak_ptr等类型的智能指针,用于解决内存管理的问题。
4)初始化列表:使用初始化列表来对类进行初始化。
5)右值引用:基于右值引用可以实现移动语义和完美转发,消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。
6)atomic原子操作用于多线程资源互斥操作。
7)新增STL容器array以及tuple

如何采用单线程的方式处理高并发
在单线程模型中,可以采用I/O复用来提高单线程处理多个请求的能力,然后再采用事件驱动模型,基于异通不回调来处理事件。(使用I/O多路复用,selete或者epool)

实例对象都是通过调用类的方法生成的。

静态变量static在不同的实例中地址一样,他们存储在全局区。
一个无序容器与其有序版本相比有何优势
无序版本通常性能更好,使用更为简单。有序版本的优势是维护了关键字的序。
当元素的关键字的类型没有明显的序关系,或是维护元素的徐代价非常高时,无序容器非常有用。
但当应用要求必须维护元素的序时,有序版本就是唯一的选择。

解释set和list的差别
1、两者都可以保存集合。
2、如果只需要顺序访问这些元素,或是按位置访问元素,那么应使用list。
3、如果需要快速判定是否有元素等于给定值,则应使用set。

map:当需要查找给定值所对应的数据时,应使用map,其中保存的是<关键字,值>对,按关键字访问值。

select、poll、epoll

Select :用户通过3个参数分别传入感兴趣的可读、可写及异常等事件,内核通过对这些参数的在线修改来反馈其中的就绪事件。使得用户每次调用select都要重置这3个参数。
应用程序索引就绪文件描述符的时间复杂度为:O(n)
最大支持文件描述符数:一般有最大值限制
工作模式:LT
内核实现和工作效率:采用轮询方式来检测就绪事件,算法时间复杂度为O(n)

Poll:统一处理所有事件类型,因此只需一个事件集参数,用户通过修改pollfd.revents反馈其中就绪的事件
应用程序索引就绪文件描述符的时间复杂度为:O(n)
最大支持文件描述符数:65535
工作模式:LT
内核实现和工作效率:采用轮询方式来检测就绪事件,算法时间复杂度为O(n)

epoll:内核通过一个事件表直接管理用户感兴趣的事件。因此每次调用epoll_wait时,无须反复传入用户感兴趣的事件。epoll_wait系统调用的参数events仅用来反馈就绪的事件
应用程序索引就绪文件描述符的时间复杂度为:O(1)
最大支持文件描述符数:65535
工作模式:支持ET高效模式
内核实现和工作效率:采用回调方式来检测就绪事件,算法事件复杂度为O(1)

C++造成内存泄露的原因

1、程序循环new创建出来的对象没有及时delete掉,导致了内存的泄露。
2、delete掉一个void*类型的指针,导致没有调用到对象的析构函数,析构的所有清理工作都没有去执行从而导致内存的泄露。
3、new创建出来的对象数组,内存回收时却调用了delete而非delete[]来处理,导致只有对象数组的第一个对象的析构函数得到了执行并回收了内存占用,数组的其他对象所占内存却得不到回收,导致内存泄露。

发布了39 篇原创文章 · 获赞 10 · 访问量 742

猜你喜欢

转载自blog.csdn.net/weixin_43393776/article/details/97824635