C与C++的区别
(1)C是面向过程语言,C++算是面向对象语言;
(2)C使用malloc/free进行申请内存或释放内存, C++在此基础上还会使用new/delete,两者的使用是有区别的,前者申请内存可以指定大小,后者则不需要;
(3)C不支持函数重载,C++是支持的;
(4)C不存在引用的概念,但是C++支持;
C++指针与引用的区别
(1)指针本身是一个地址,该地址指向的就是存储的变量,修改地址对指向的对象没有影响;引用可以理解为变量的别名,对引用的修改就是对变量的修改,变量前加上 &求址运算符就可以获得其地址,也就是变量指针指向的位置;
(2)指针可以扩充很多级,例如int**** p这是对的,但是引用只可以int& p;
内联函数
概念:函数的前面添加inline修饰符,c++在编译的时候会在调用内联函数的地方自动展开,而无需像调用其他函数一样,有进栈的开销;内联函数是用空间换时间,提高程序的运行效率;
技巧: (1)内联函数一般是不超过10行;
(2)复杂的内联函数应放到后缀名为.inl.h的头文件中;
宏定义的优缺点
优点:增强代码复用性;提高性能;
缺点:不方便调试宏(因为在预编译阶段就完成了替换);代码可读性差,可维护性差;没有类型安全检查;
C++有哪些技术可以替换宏
(1)常量定义 换用const;
(2)函数定义 换用内联函数
(3)类型重定义 换用typedef
结构体struct与联合体union的区别
结构体是不同的数据类型变量组成的一个整体,是自定义类型;
联合体是几个不同类型的数据变量共同占用一段内存;
(1)结构头每一个变量都有自己的内存地址,同时存在,但是联合体是所有的数据变量共同占用一段内存,不能同时存在;
(2)用sizeof求结构体的大小,是所有数据变量的占用内存大小的总和,但是联合体是内存对其后最长数据成员的长度;
#define宏定义与const常量的区别
(1)宏定义的常量是一个立即数,没有数据类型,但是常量是有数据类型的,存放在静态存储区域;
(2)宏定义是预编译阶段确定,可能由多个拷贝;常量是有编译阶段确定值,只有一个拷贝;
(3)宏定义是不能用指针指向的,但是指针可以指向一个常量;
(4)宏兴义可以定义简单函数,常量不可以定义函数;
重载(overload),重写(overwrite),覆盖(override)的区别
(1)重载是发生一个类里面,同名的函数,不同的参数列表和返回值;
(2)重写发生父类与子类里面,父类与子类的同名函数,相同的参数列表与返回值,但是具体实现不同;
(3)覆盖也是发生父类与子类,但是必须是虚函数,同名的虚函数,相同的参数列表与返回值,实现接口重用;
new/delete与malloc/free的区别
两个都是申请内存与内存释放的方式;
(1)malloc开辟空间大小严格指定,new后面只需要跟对象名;
(2)new一个新对象时会调用类的构造函数,用delete时会调用对象的析构函数;
delete与delete[]的区别
(1)delete对象会用析构函数,delete[]会调用每一个对象成员的析构函数;
(2)new用delete,new[]用delete[];
STL的相关知识,常见的STL容器,算法有哪些?
(1)STL是标准模板库,是C++高效的程序库;
(2)一个重要的特点:数据结构与算法分离,例如用sort()函数可以适用于任何数据集合;
(3)一个重要特性:不是面向对象的,STL是基于模版的,所以通用性很好;
(4)STL六大组件:容器,迭代器,算法,仿函数,适配器,分配器;
(5)容器主要是两大类:序列式容器(vector,deque,list),内部不一定有序,但是都可以进行排;关联式容器(set/multiset, map/multimap),内部是一个平衡二叉树,每个元素是键值对;
(6)算法:四类:非可变序列算法,可变序列算法,排序算法,数值算法(针对容器内数值进行计算)
(7)迭代器:提供一种方法,去访问容器中每个元素,而无需暴露内部的数据结构;
(8)仿函数:就是使一个类的使用看上去像使用一个函数;
(9)适配器:对容器进行包装,将其展现出另外一种行为;
const的作用
(1)const修饰变量,表示变量不可被修改;
(2)const修饰函数,表示函数不会修改类的成员函数,不可以调用非const成员函数;
虚函数的实现
(1)虚函数是通过虚函数表实现,每一个包含虚函数的类都会有一个与之对应的虚函数表,虚函数表中存放的是指向函数的指针;
(2)类的实例对象没有虚函数表,只有虚指针;
(3)派生类会生成一个兼容基类的虚函数表;
栈和堆的区别
(1)在C++中栈是用来存放函数的参数值以及临时变量,由编译器自动分配释放;堆是由new申请分配的内存块,由应用程序控制,由开发人员手动释放,如果没有手动释放则在程序结束时由操作系统自动回收;
(2)堆的分配需要频繁的new/delete,会造成空间不连续,产生大量碎片;
(3)堆的生长空间向上,地址越来越大;栈的生长空间向下,地址越来越小;
#include
前者是在标准库路径下寻找;后者是在当前路径下寻找;
什么是内存泄露,有哪些方法可以防止内存泄漏?
(1)内存泄漏是️动态申请的内存在使用结束之后没有释放,导致该内存一直被占用,这就是内存泄漏;
(2)方法:malloc/free,new/delete必须配套使用;
声明与定义的区别
(1)声明是告诉编译器变量的类型与名字,编译器并不会为变量分配内存;
(2)定义是会进行分配内存空间的;声明可以声明多次,但是只能定义一次;
C++文件编译的四个阶段
(1)预处理:将头文件编译进来,完成宏的替换;(.c文件->.i文件)
(2)编译:将代码转换成汇编语言;(.i文件->.s文件)
(3)汇编:将汇编语言的代码转换成机器语言;(.s文件->.o文件)
(4)链接:链接代码生成可执行文件;
vector使用的注意点
(1)vector就是一个动态增长的数组,内部是一个指针指向一片连续的数组,当空间装不下的时候,会申请一片更大的内存,然后将原来的数据拷贝过去,并释放原来的内存空间;一般是1.5倍或者2倍扩容,要看编译器;
(2)当原来的内存空间不够使用,申请新的内存进行拷贝,这种操作是比较消耗性能的;
unorder_map与map的区别
(1)都是存放键值对的,map内部的数据是有序的,unorder_map不会根据key进行排序,内部是无序的;
(2)map底层是红黑树,unorder_map底层是一个防冗余的哈希表;
C++的内存管理
C++中内存被分为五个区域,栈,堆,自由存储去,静态存储区(全局存储区),常量区;
(1)栈是用来存放函数的参数,局部变量;
(2)堆是由new申请进行动态分配的内存;
(3)自由存储区是由malloc申请进行动态分配的内存;
(4)全局/静态存储区存放全局变量,静态变量;
(5)常量区存放常量;
构造函数为什么不能声明为虚函数?
(1)从存储空间角度:虚函数是对应一个虚函数表,这个虚函数表是存储在对象中,但是在没有实例化之前是不会分配内存的,所以就没有办法存储虚函数表,所以构造函数不能是虚函数;
(2)使用意义:虚函数的作用是在父类的指针或者引用调用函数时,变成调用子类的成员函数,而构造函数是在实例化对象,声明成虚函数没有意义;
析构函数为什么最好声明成虚函数?
(1)当析构一个指向派生类的基类的指针时,则可以优先调用派生类的析构函数,避免内存泄漏;
(2)若析构函数不是虚函数,则编译器静态绑定,当析构一个指向一个派生类的基类的指针,只会调用基类的析构函数,不会调用派生类的析构函数,则析构不完全;
静态绑定与动态绑定
(1)静态绑定是在编译时期,方法被关联到方法体;动态绑定是程序在运行过程中,方法被关联到方法体的过程;
(2)C++只有虚函数使用动态绑定,其余均使用静态绑定;
浅拷贝与深拷贝
如果一个类拥有资源,类的对象在发生复制过程中,如果重新分配资源则进行了深拷贝,如果没有重新分配资源则是浅拷贝;
使用到拷贝构造函数的三种情况:
(1)用一个类的对象去初始化另一个对象时;
(2)函数的参数是类对象时,也就是值传递;
(3)函数的返回值是类对象或者是引用的时候;
引用作为参数传递的好处
(1)传递引用可以在函数内部对原值进行修改;
(2)效率更高,值传递过程中生成副本会有时间和空间消耗;
纯虚函数的知识点
(1)纯虚函数是只有声明没有实现的虚函数,是对派生类的约束,是接口继承;
(2)包含纯虚函数的类是抽象类,不能实例化,只有实现这个函数的派生类才可以实例化对象,如果没有实现的化仍然是派生类;
什么是野指针
野指针不是NULL指针,而是未初始化或者是未清零的指针,指针指向的内存不是开发者希望的,可能指向了受限的内存;
线程安全与线程不安全
(1)线程安全就是多线程访问时,采用加锁机制,当某一个线程使用一个临界资源时,其他线程在等待,只有当前线程释放之后才可以被其他线程使用,不会造成数据不一致或者是数据污染 ;
(2)线程不安全就是不提供数据保护访问,有可能多个数据同时访问一个临界资源,导致读取的是脏数据;
C++内存泄漏的几种情况
(1)类构造函数中的new与析构函数的中delete没有配套;
(2)使用new[]时没有配套的使用delete[],使用了delete
(3)当基类的指针指向派生类对象时,基类的析构函数不是虚函数,当对象删除时,调用的是基类的析构函数而不是派生类的析构函数,所以会导致析构不完全;
(4)有嵌套的对象指针未被释放;
C++11新特性
(1)引入auto关键字,进行自动类型推导;
(2)引入decltype关键字,类似auto,从变量或者表达式中得到类型;
(3)引入nullptr关键字,解决c++原本中NULL的二义性
(4)加入lambda表达式;
C++中vector与list的区别
(1)vector申请的是一段连续的内存,当内存不够的时候会进行二倍扩容,然后拷贝到新内存空间,释放原来的内存空间;vector更像数组,支持随机存取,但是插入或者删除会造成内存块的拷贝;
(2)list是一个双向链表,空间不连续,通过指针去访问数据,不能随机存取;能够进行高效的插入或者删除操作;
友元函数与友元类
(1)友元函数是定义在类外的普通函数,可以访问类的私有成员的函数,但不属于任何类,需要在类的定义中加以声明,一个函数可以多个类的友元函数;
(2)友元类的所有函数都是另一个类的友元函数,都可以访问另一个类的私有信息;
注意:
(1)友元类在进行派生时,友元关系是不可以进行派生的,也就是友元关系不能被继承;
(2)友元关系是单向的,不具有交换性;B类是A的友元,但是A不一定是B的友元;
(3)友元关系不具有传递性,A是B的友元类,B是C的友元类,但是A不一定是C的友元类;
C++的锁机制
(1)互斥锁:用于多个线程访问共同资源进行互斥访问的的一个信号量,某个时刻只可以有一个线程获取互斥锁,访问共享资源;
(2)条件锁:就是条件变量,某一个线程因为满足某一个条件时可以用条件锁改变程序的阻塞状态;
(3)自旋锁:线程会循环请求获取这个锁,而互斥锁是在线程在获取不到锁的情况下会去做其他事情,而自旋锁则是等待循环请求;自旋锁适合短时间轻量级的加锁机制;
(4)读写锁:可以有多个读操作,但是某一时刻确定的只有一个写操作;