C++知识点复习(持续更新....)

1.缺省参数需要注意什么?

必须从右往左给,不能间隔
缺省值必须是常量或者全局变量
C语言不支持(编译器不支持)

2.什么是重载?为什么C++支持C不支持呢?

重载就是在同一作用域下函数名相同,参数(个数,顺序,类型)不同的函数,常用来处理功能相似,数据类型不同的问题
C语言同名函数是没有办法区分,C++采用函数修饰规则来区分,只要参数不同修饰出来的名字就不同,因此支持了重载

3.extern"C"是什么?

将函数按照C语言规则来修饰
则调用时就不使用C++规则的带有参数信息的名字,而是_fun,从而达到调用C函数的目的

4.引用是什么?引用有哪些特性?

引用不是定义一个新变量,而是给已存在的变量取一个别名
引用在定义时必须初始化
一个变量可以有多个引用
引用一旦引用一个变量,就不能再引用别的变量

5.引用和指针的区别?

引用初始化时引用一个变量就不能改变了,而指针可以在任何时候指向任何实体
sizeof()不同,引用是引用实体类型大小,而指针始终是地址空间大小(32位平台下是4个字节)
引用必须要初始化,指针可以不用
指针可以指向NULL,引用不行
引用自加是实体自加,指针自加是指针向后偏移一个类型大小
有多级指针,没有多级引用
访问实体方式不同,指针需要解引用,引用编译器自己处理

6.inline函数是什么?作用?特性?

inline函数被叫做内联函数,在编译时C++编译器会在调用内联函数处展开,没有函数压栈的开销,提升了程序运行的效率
以空间换时间----代码很长或者有循环递归的函数不宜用内联函数
inline不建议声明和定义分离,会导致链接错误.—内联函数被展开,没有函数地址,链接就会失败

7.C++有哪些技术可以替代宏?

常量定义 换用const
函数定义 换用内联函数

8.宏的优缺点?

优点:
增强代码的复用性
提高性能
​ 缺点:
不方便调试宏
导致代码可读性差,可维护性差,容易误用
没有类型安全检查

9.nullptr和NULL的区别?

NULL在C++就是0,因为在C语言中把空指针赋给int和char指针的时候,发生了隐式类型转换,把void指针转换成了相应类型的指针。
int *pi = NULL;
char *pc = NULL;
而C++不行,不能隐式转换成其他类型的指针
nullptr就是空指针(C++11)

10.C++11特性有哪些?

for循环,auto关键字,指针空值(nullptr)

11.面向对象三大特性是什么?分别说一下你对他们的理解

​ 封装,继承,多态
​ 封装:将数据和操作数据的方法进行结合,仅对外公开接口和对象进行交互。
​ 继承:继承就是新类从已有类那里得到已有的特性,类的派生指的是从已有类产生新类的过程。原有的类成为基类或父类,产生的新类称为派生类或子类。
​ 多态:“一个接口,多个实现”

12.说一下你对面向过程和面向对象的理解

面向过程:关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题

​ 面向对象:关注的是对象,将一件事情分解成不同的对象,靠对象之间的交互完成

13.c++中struct 和class的区别是什么?

struct访问方式默认是public,class访问方式默认是private

14.类成员变量,类成员函数存放在哪里?

每个对象只保存成员变量,成员函数放在公共的代码段
所以计算类大小只计算成员变量,不计算成员函数

15.空类的大小?

编译器给空类一个字节来唯一标识这个类

16.this指针的特性?

只能在成员函数内部使用
本质是一个成员函数的形参,对象调用成员函数时,将对象的地址作为实参传入
this指针是成员函数第一个隐含的指针形参,由编译器通过ecx寄存器自动传递

17.this指针存在哪里?

编译器会在生成程序时加入了获取对象首地址的相关代码,并把获取的首地址存放在寄存器ECX,类的静态成员函数因为没有this指针,所以类的静态成员函数也就无法调用类的非静态成员变量

18.this指针可以为空吗?

可以为空,在函数内部不需要使用this的时候是可以的,但是如果调用的函数需要指向当前对象,则发生错误(空指针引用)

19.默认构造函数

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认成员函数。

20.深浅拷贝?

在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,否则构造一次,析构两次,造成内存泄露

21.拷贝构造函数?

传参使用const+引用传参,传值拷贝会引发无穷递归调用

22.const和static的作用

​const的作用: ​
限定变量为不可修改。 ​
限定成员函数不可以修改任何数据成员。用法如下:
int GetCount(void) const;----在函数末尾加const关键字 ​
const与指针: ​
const char *p 和char const *p都表示 指向的内容不能改变。 ​
char * const p,就是将P声明为常指针,它的地址不能改变,是固定的,但是它的内容可以改变。

​ static:

​静态成员为所有类对象所共享,不属于某个具体的实例

静态成员变量必须在类外初始化,定义时不添加static关键字(类型 类::成员变量=…)

​类静态成员即可用类名::静态成员或者对象.静态成员来访问

​静态成员函数没有隐藏的this指针,不能访问任何非静态成员

​静态成员和类的普通成员一样,也有public、protected、private3种访问级别,也可以具有返回值

23.哪5个运算符不能重载?

.* 、:: 、sizeof 、?: 、.

24.初始化列表的特性?

初始化列表才算是初始化
每个成员变量在初始化列表出现一次(初始化只能初始化一次)
引用成员变量(int &ref),const成员变量(const int _n),自定义类型成员(该类没有默认构造函数)
成员变量在类中的声明次序就是其在初始化列表中的初始化顺序,和在初始化列表中的先后次序无关
除了性能问题之外,有些时候合初始化列表是不可或缺的,以下几种情况时必须使用初始化列表
1.常量成员,因为常量只能初始化不能赋值,所以必须放在初始化列表里面
2.引用类型,引用必须在定义的时候初始化,并且不能重新赋值,所以也要写在初始化列表里面
3.没有默认构造函数的类类型,因为使用初始化列表可以不必调用默认构造函数来初始化,而是直接调用拷贝构造函数初始化

25.友元?

友元函数可以直接访问类的私有成员,定义在类外,不属于任何类,需要在类内部声明,声明时加上friend关键字
友元函数可访问类的私有和保护成员,但不是类的成员函数
友元函数不能用const修饰
友元函数可以在类定义的任何地方声明,不受类访问限定符限制
一个函数可以是多个类的友元函数
友元函数的调用与普通函数的调用和原理相同

26.new和malloc的区别?

malloc/free和new/delete的共同点是:都是从堆上申请空间,并且需要用户手动释放。
malloc和free是函数,new和delete是操作符

​ malloc申请的空间不会初始化,new可以初始化

malloc申请空间时,需要手动计算空间大小并传递,new只需在其后跟上空间的类型即可

malloc的返回值为void*, 在使用时必须强转,new不需要,因为new后跟的是空间的类型

malloc申请空间失败时,返回的是NULL,因此使用时必须判空,new不需要,但是new需要捕获异常

申请自定义类型对象时,malloc/free只会开辟空间,不会调用构造函数与析构函数,而new在申请空间后会调用构造函数完成对象的初始化,delete在释放空间前会调用析构函数完成空间中资源的清理

27.什么是内存泄露?如何解决?

内存泄露是指因为疏忽或错误造成程序未能释放已经不再使用的内存情况,失去对该内存的控制,造成了浪费
智能指针

28.实现一个模板函数或者模板类

template<typename T>
//template<class T>
void swap(T& left,T& right){
    
    
    T temp=left;
    left=right;
    right=T;
}

29.了解一下为什么模板不支持分离编译

调用的Add和Add,编译器只有在链接时找地址,这两个函数没有实例化生成具体的代码,因此链接时报错。

30.什么是特化?在哪里用过特化?

为已有的模板参数进行一些使其特殊化的指定,使得以前不受任何约束的模板参数现在指定下来

//泛化:
template <class Key>
struct hash{
    
     };

//特化:
template<>
struct hash<char>
{
    
    
    size_t operator() (char x) const {
    
     return x; }
};

template<>
struct hash<int>
{
    
    
    size_t operator() (int x) const {
    
     return x; }
};

template<>
struct hash<long>
{
    
    
    size_t operator() (long x) const {
    
     return x; }
};

31.vector是什么?vector和list的区别与联系

vector:是可动态增长的数组容器
list:可以在任意位置插入删除的带头双向链表序列容器
vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。
vector缺点:头插或者中间插入效率低。
list缺点:空间利用率低,无法随机访问

32.vector插入数据是如何增容的?为什么是2倍增容? 注意增几倍都可以,一般增多了,空间浪费大,增少了,频繁增容,效率低

vs下是1.5倍增容,g++下是2倍增容。

33.面试最常考察:什么是迭代器?什么是迭代器失效?

vector
迭代器本质就是一个指针,vector的是原生指针*T,
迭代器失效就是迭代器指针指向的位置已经释放,继续使用一块被释放的空间,会造成程序崩溃
vector迭代器失效原因:
会引起其底层空间改变的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。
出错原因:以上操作,都有可能会导致vector扩容,也就是说vector底层原理旧空间被释放掉,而在打印时,it还使用的是释放之间的旧空间,在对it迭代器操作时,实际操作的是一块已经被释放的空间,而引起代码运行时崩溃。
指定位置元素的删除操作-erase
erase删除pos位置元素后,pos位置之后的元素会往前搬移,没有导致底层空间的改变,理论上讲迭代器不应该会失效,但是:如果pos刚好是最后一个元素,删完之后pos刚好是end的位置,而end位置是没有元素的,那么pos就失效了。因此删除vector中任意位置上元素时,vs就认为该位置迭代器失效了。
迭代器失效解决办法:在使用前,对迭代器重新赋值即可。

if(*it%2==0){
    
    
	it=v.erase(it);//重新赋值
}
else{
    
    
	it++;
}

list:只有删除才会存在迭代器失效,只是指向被删除节点的迭代器失效,其他迭代器不会受到影响。

//l.erase(it++); 
it=l.erase(it);

34.c和c++的区别?面向对象和面向过程的区别?

c是面向过程的。c++是面向对象的

​ 面向对象是首先抽象出各种对象(各种类),把数据和方法都封装在对象中(类),然后各个对象之间发生相互作用。

​ 面向过程是将问题分解成若干步骤(动作),每个步骤(动作)用一个函数来实现,在使用的时候,将数据传递给这些函数。

35.c++缺点

继承C语言绝大部分语法,c的缺陷就过来了,比如复杂的指针

​ c++的语法比较复杂,比如有多继承,没有GC,引入智能指针

​ c++库更新慢

​ 补充:多继承可以认为是C++的缺陷之一

36.什么是继承?继承的作用是什么?

继承就是使新类拥有已有类的已有特性
继承呈现了面向对象程序设计的层次结构

37.重载?重定义(隐藏)?重写(覆盖)?

重载:在同一作用域,函数名相同,参数不同构成重载
重定义:在不同作用域,函数名相同即可构成重定义
重写:在不同作用域,函数名,参数,返回值相同,两函数必须是虚函数

38.友元继承特点?static继承有什么特点?

友元关系不能继承,也就是说友元并不能访问派生类私有成员变量
static成员,不管有多少子类,都只有一个static成员

39.什么是菱形继承?菱形继承的问题是什么?怎么解决?(虚继承) 解决的原理是什么?

菱形继承:多继承的一种,b,c继承a,d又继承b,c,就构成了菱形继承
数据二义和数据冗余
虚继承(virtual),通过虚基表指针指向虚基表,虚基表有偏移量,通过偏移量指向那个位置

40.什么是is-a,什么是has-a?继承和组合哪个更好?为什么?

is-a:public继承,也就说每个派生类对象都是一个基类对象,白箱复用

​ has-a:组合,假设B组合了A,每个B对象都有一个A对象,黑箱复用

​ 继承耦合度高,组合耦合度低。

41.什么是多态?多态的条件是什么?多态实现的原理是什么?

多态就是一个接口,多种实现,当不同对象去完成时会产生不同的结果
继承
必须通过基类指针或引用调用派生类对象
必须都是虚函数
不同对象通过虚函数表指针找到虚函数表,虚函数表(虚表)存放着虚函数指针,通过指针找到对应的虚函数

42.inline函数、构造函数、析构函数、static函数是否可以是虚函数?

inline函数可以是虚函数吗?答:不能,因为inline函数没有地址,无法把地址放到虚函数表中。
static函数可以是虚函数吗?答:不能,因为静态成员函数没有this指针,使用类型::成员函数的调用方式无法访问虚函数表,所以静态成员函数无法放进虚函数表。
构造函数可以是虚函数吗?答:不能,因为对象中的虚函数表指针是在构造函数初始化列表阶段才初始化的。
析构函数可以是虚函数吗?什么场景下析构函数是虚函数?答:可以,并且最好把基类的析构函数定义成虚函数。

43.虚函数表是在什么阶段生成的,存在哪的?

答:虚函数表是在编译阶段就生成的,一般情况下存在代码段(常量区)的。

44.什么是抽象类?抽象类的作用?

抽象类强制重写了虚函数,另外抽象类体现出了接口继承关系。
抽象类限制了基类实例化对象,如果派生类不重写虚函数,也无法实例化对象
虚函数后面=0就是纯虚函数,有纯虚函数的类叫虚函数类

45.二叉搜索树概念

又叫二叉排序树
左子树所有节点小于根节点
右子树所有节点大于根节点
它的左右子树也分别为二叉搜索树

46.map和set区别?增删查改时间复杂度是多少?

set
set只放value,底层是value:value构成的键值对
set中元素是不可以重复的(因此可以使用set去重)
使用set迭代器遍历set元素,可以得到有序序列
set中的元素不可以修改,类型const
底层红黑树实现
map
<key,value>结构存储
map中元素总是按着键值key进行排序的
使用map迭代器遍历map元素,可以得到有序序列
map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
当key不存在时,operator[]用默认value与key构造键值对然后插入
map中的key是唯一的,并且不能修改

47.map和multimap的区别?

multimap和map的区别在于,multimap中的key可以重复
multiset和set的区别在于,multiset中的key可以重复

48.什么是红黑树?红黑树和AVL树的区别?规则是什么?

红黑树:是一颗平衡二叉树,每个结点都有一个存储为来表示它的颜色(红色或者黑色),红黑树确保没有一条路径是其他路径的二倍
红黑树和AVL树都是高效的平衡二叉树,复杂度都一样。红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数
​ 红黑树规则:根节点是黑色的,叶子节点(null)是黑色的,每个结点不是黑色就是红色,一个结点是红色的,他的两个子节点一定是黑色的,对于每个结点,从该节点到所有后代叶节点的简单路径上,均包含相同数目的黑节点

48.unordered_map和unordered_set跟map/set的区别?

一个底层是哈希表,时间复杂度O(1),一个是红黑树O(logN)
哈希表遍历出来是无序,而map/set遍历出来是有序的

49.什么是哈希/散列?什么是哈希冲突?如何解决?开放定制法(闭散列) + 拉链法/哈希桶(开散列)

构建一种存储结构,通过函数使元素的存储位置与关键码建立一一对应的关系
不同的关键字通过相同哈希函数计算出相同的哈希地址,会导致哈希冲突
开放定制法:
线性探测:依次向后探测,知道找到下一个空位置
二次探测:+i^2(i=1,2,3,…)%m;
拉链法
将相同地址的关键码用单链表连接起来,各列表头结点存到哈希表中

50.如果哈希表冲突厉害怎么办?

控制负载因子
使用拉链法,如果一个冲突链达到一定长度就可以将链表换成红黑树

51.位图优缺点?

每一位来存放某种状态,适用于海量数据
优点:高效,可以解决海量数据
缺点:只能处理整形数据

52.布隆过滤器

用多个哈希函数,将一个数据映射到位图结构中。此种方式不仅可以提升查询效率,也可以节省大量的内存空间。
优点:高效,处理海量数据,节省空间,可以存储任意类型
缺点:存在误判,不支持删除
误判是判断数据在的时候,测试数据不在不会发生误判

53.异常是什么?异常优缺点?

是面向对象语法处理错误的一种方式
被选中的处理代码是与该对象类型匹配且离抛出异常位置最近的那一个
优点:
可以清晰包含错误信息
多层调用时,里面发生错误,外层直接捕获
面对函数越界错误,异常很好解决
缺点:
导致执行流乱跳,分析问题困难
可能会导致资源泄露等异常安全问题
c++库定义的异常不太好用

54.异常安全问题?

不要再构造函数抛出异常,可能会导致对象不完整或没有完全初始
不要再析构函数抛出异常,可能会导致资源泄露
使用智能指针解决资源泄露

54.什么是RAll,什么是智能指针?为什么需要智能指针?

RAll是通过对象的生命周期控制程序资源的一种思想
智能指针是RAll+重载operator*和operator->(具有像指针一样的行为)
为了避免发生内存泄露,泄露有创建空间后忘记释放,还有异常安全问题都会导致内存泄露
智能指针只是受人托管资源,可以访问空间,比原生指针多的是,可以在生命周期释放资源
auto_ptr缺陷:
1.一个指针变量指向的空间不能由两个auto_ptr管理,不然析构两次,程序崩溃
2.auto_ptr的拷贝构造,将原指针的管理权转移目标指针,原指针置空
3.不能管理数组,析构函数中用的是delete
unique_ptr
1.不能进行拷贝构造,管理的对象不能共享所有权,功能不全面
shared_ptr
加入引用计数,很好的解决了auto_ptr释放两次空间的温调
55.shared_ptr的循环引用?如何解决?解决原理是什么?
指多个对象相互引用,使得引用形成一个环
解决原理:weak_ptr。在引用计数场景下,把节点中的_prev 和_next改成weak_ptr
weak_ptr计数不会增加

56.特殊类设计

57.四种类型转换

static_cast:用于隐式类型转换(不能用于不相近类型)
reinterpret_cast:用于不相近类型转换
const_cast:去除const属性类型转换
dynamic_cast:只能用于含有虚函数的类,父类向子类转换成功则转换,不能则返回0

58.右值引用和左值引用?

左值引用是引用的是变量才能叫左值引用
右值引用引用的是常量,表达式或者函数返回值(临时对象)
右值引用做参数或者返回值减少拷贝本质是利用了移动构造和移动赋值
左值引用:解决的是传参过程和返回值过程中的拷贝
右值引用:解决的是传参后,push/insert函数内部将对象拷贝到容器空间上的问题+传值返回接收返回值的拷贝
右值引用原因:
代替需要销毁对象的拷贝,提高效率
移动含有不能共享资源的类对象(unique_ptr)

59.完美转发的问题?

右值引用会在第二次之后的参数传递过程中右值属性丢失,下一层调用会全部识别为左值
完美转发解决问题:在第二次调用的时候使用std::forword(t)

60.lambda表达式

[捕获列表] (参数列表) mutable ->return-type{statement}
捕捉列表:能够捕捉上下文的变量供lambda函数使用
参数列表:传参
mutable:取消const(常量性)
->returntype:返回值类型
{statement}函数体

实际在底层编译器对于lambda表达式的处理方式,完全就是按照函数对象的方式处理的,

Guess you like

Origin blog.csdn.net/Hedenghui777/article/details/115008522