C++基础
(1)C和C++的区别
C++分为:
C部分(区块、语句、预处理器、内置数据类型、数组、指针等);
面向对象的C++特性(类、封装、继承、多态、动态绑定等);
模板特性(泛型编程、元编程);
STL(容器、迭代器、算法、函数对象等);
参考:《effective C++》 条款01
(2)指针和引用
指针:存放变量地址值的变量。
引用:变量的别名。
(特性)1、引用定义时需要初始化,指针不用;
(特性)2、引用无法改变绑定对象,指针可以;
(内存)3、sizeof(引用) = sizeof(type),sizeof(ptr) = 4/8;
(3)堆和栈
栈:存放变量的特定地址空间。
堆:存放变量的特定地址空间。
(内存)1、栈向下生长;堆向上生长;
(内存)2、栈由系统自动分配空间,机器底层级别提供支持(方式),空间固定较小~2M,内存利用率更高(效率);堆由malloc、new控制,C/C++库函数级别提供支持,空间更大,链表管理,容易产生碎片;
参考:https://blog.csdn.net/nieyibin/article/details/7468323
(4)new和delete
new:C++运算符,在堆区申请内存,返回指针。
delete:C++运算符,回收指针指向内存空间。
(执行步骤)1、调用operator new申请内存;2、调用构造函数;3、调用析构函数;4、调用operator delete释放内存;
参考:https://blog.csdn.net/hihozoo/article/details/51441521
(5)new和malloc
new:C++运算符,在堆区申请内存,返回指针。
malloc:C库函数,在堆区申请内存,返回指针。
(特性)1、new首先申请内存,然后调用构造函数,不需要指定内存空间大小;malloc需要手动指定内存空间大小;
(特性)1、new []/delete []用于申请/释放数组空间;malloc没有数组空间的概念;
(特性)2、new失败返回bac_alloc;malloc失败返回NULL;
(安全)3、new类型安全;malloc返回void*,非类型安全;
(特性)4、new过程中的operator new可以重载;malloc不能重载;
(优化)5、operator new是操作符能经编译器优化;malloc是库函数,不受编译器优化。
参考:
https://www.cnblogs.com/QG-whz/p/5140930.html
https://chenqx.github.io/2014/09/25/Cpp-Memory-Management/
(6)struct和class的区别
struct:C语言提供的数据封装;
class:C++扩展的数据封装;
(特性):struct成员默认权限为public,struct继承方式默认为public;class成员默认权限为private,class继承方式默认为private;
参考:https://stackoverflow.com/questions/92859/what-are-the-differences-between-struct-and-class-in-c
(7)define 和const
define:宏定义,预处理期间进行替换;
const:关键字,修饰变量,限制变量为常量类型;
(阶段)1、define为预处理期间执行替换;const为编译期分配内存;
(安全)2、define不进行类型检查;const会进行类型检查;
(内存)3、define直接替换,如果是字面值则被编译器处理为立即数,占据内存空间不定,取决于编译器和操作系统;const则占据静态存储区中的.DATA段,因为定义时需要初始化;
(使用)4、define替换的变量不好追踪;const变量在符号表中,方便追踪;
参考:
https://www.geeksforgeeks.org/diffference-define-const-c/
《effective C++》条款02
(8)static和const
const:修饰变量,表示其为常量;
static:修饰变量,表示变量驻留在静态存储区,不回收其内存,限制其作用域;
用于变量:
(最重要)1、const全局变量作用域为所有文件(外部链接);static const全局变量作用域为本文件(内部链接);总结:static起到了限制作用域的作用;(注意,以上情况是C,在C++中const修饰全局变量默认为内部链接等同于static const)
(内存)2、const全局变量,符号表或静态存储区.DATA;const局部变量,栈区;static全局变量,静态存储区.BSS/.DATA;static局部变量,栈区;
参考:
(const变量)https://blog.csdn.net/xiazhiyiyun/article/details/71969618
(static变量)https://www.cnblogs.com/cobain/archive/2008/01/31/1060216.html
(const全局变量)http://mzorro.me/2013/04/13/diff-of-const-in-c-and-cpp/
(全局变量和局部变量)https://blog.csdn.net/qq_33266987/article/details/51965221
用于类内部:
(特性)1、类中的static成员属于类,也即属于所有对象,也即没有this指针;类中的const成员属于对象/实例;
(初始化)2、类中的static成员在类外初始化,且先于各个对象的初始化;类中的const变量需要使用初始化列表进行初始化;
(函数成员)3、类中的static函数只能使用类中的static变量;类中的const函数表示该函数执行不改变对象中的各个变量的值;
参考:
(static成员)https://blog.csdn.net/u014453898/article/details/64124269/
(9) 计算下面几个类的大小:
class A {};: sizeof(A) = 1;
class A { virtual Fun(){} };:
sizeof(A) = 4(32位机器)/8(64位机器);
class A { static int a; };: sizeof(A)
= 1;
class A { int a; };: sizeof(A) = 4;
class A { static int a; int b; };:sizeof(A) = 4;
(10)重载(overload)、覆盖(override)、隐藏(hidden)
重载:同一类中,函数名相同,参数类型/数量不同;
覆盖:不同类中,函数名相同,参数相同,虚函数;
隐藏:不同类中,函数名相同,参数相同,非虚函数;
不同类中,函数名相同,参数不同,虚函数或非虚函数;
参考:http://www.cnblogs.com/txwsh1/archive/2008/06/28/1231751.html
(11) C ++内存分配
从C++语言角度看:
栈:存放局部变量,函数执行结束时释放。内存分配运算内置于处理器的指令集中,效率高,空间有限。
堆:new分配的内存块,由delete控制释放。
自由存储区:malloc分配的内存块,由free控制释放。
全局/静态存储区:存放全局变量、静态变量。C语言中,全局变量分为初始化的(.DATA)和非初始化的(.BSS);C++中没有区分。
常量存储区:存放常量,不允许修改。
程序代码段:存放函数体的二进制代码。
从linux程序的内存映像角度看:
参考:
(基础概念)https://chenqx.github.io/2014/09/25/Cpp-Memory-Management/
(基础概念)https://www.cnblogs.com/dong008259/archive/2011/11/07/2239353.html
(堆、自由存储区的区别)https://www.cnblogs.com/QG-whz/p/5060894.html
(linux内存)https://blog.csdn.net/shanghairuoxiao/article/details/70256247
(linux内存)http://mqzhuang.iteye.com/blog/901602
(12) 面向对象的三大特性
封装:将数据和方法封装到一个单元内,通过接口和外界交互,内部对外界不可见,隐藏数据与方法。
继承:一个类的对象获取另一个类的对象的属性的过程。功能添加。
多态:采取多种形式的能力,操作在不同的实例中表现出不同的行为,接口重用。C++支持运行时多态(虚函数),编译时多态(模板/泛型、函数重载)。
参考:
(概念)https://www.geeksforgeeks.org/basic-concepts-of-object-oriented-programming-using-c/
(扩展)https://www.ntu.edu.sg/home/ehchua/programming/cpp/cp3_OOP.html
(多态)http://huqunxing.site/2016/09/08/C++%20%E4%B8%89%E5%A4%A7%E7%89%B9%E6%80%A7%E4%B9%8B%E5%A4%9A%E6%80%81/
(13) C++虚函数
虚函数是应在派生类中重新定义的成员函数,当指针或基类的引用用来引用派生类的对象时,可以为该对象正确调用该函数的派生类版本。
(实现原理)1、虚指针vptr,虚函数表vtable。注意:a、同一类的不同对象共享一张vtable;该表在编译期间由编译器创建;b、各种情况:派生类单继承覆盖基类虚函数、派生类单继承不覆盖基类虚函数引入新的虚函数、派生类多继承覆盖基类虚函数等。
(特殊用法)2、抽象类/纯虚函数。注意:a、虚函数=0为纯虚函数;b、包含纯虚函数的类为抽象类,抽象类不能实例化;
(特殊用法)3、final specifier,检查确保父类函数为虚函数,且不能重载奔类中的该函数。override
specifer,检查确保是否覆盖了父类虚函数。
参考:
(概念)https://msdn.microsoft.com/zh-cn/library/0y01k918.aspx
(虚指针和虚函数表)https://blog.twofei.com/496/
(final/override)https://en.cppreference.com/w/cpp/language/override
(14) 析构函数一般写成虚函数的原因
操作基类指针释放对象内存时,将析构函数写为虚函数,可以在析构过程中首先释放派生类部分的内存,再依次释放基类部分的内存。这样能保证销毁整个对象同时不出现内存泄漏。
参考:《effective C++》条款07
(15) 构造函数为什么一般不定义为虚函数
从原理角度:虚指针是在构造函数执行过程中初始化的,编译器处理,对程序员透明。如果构造函数为虚函数,那么在开始执行构造函数时由于虚指针还未初始化,从而会出现无法定位构造函数的错误情况。
从设计思想角度:虚函数的调用只需要部分的信息,而不需要知道对象的具体类型;但是构造函数需要知道具体的类型。
(16) 构造函数或者析构函数中调用虚函数会怎样
构造函数中调用:可能出现要调用的函数未创建的情况,此时即视对象为基类,虚函数不起作用;基类构造函数执行先于派生类构造函数执行,调用基类构造函数中的虚函数,意味着调用时派生类还未创建那么该虚函数指向的总是基类该函数而不是派生类该函数;
析构函数中调用:可能出现要调用的函数已经被销毁的情况,此时即视对象为基类,虚函数不起作用;
参考:《effective C++》条款09
(17) 静态绑定和动态绑定的介绍
静态绑定(编译时多态):模板泛型、函数重载等
动态绑定(运行时多态):虚函数等
参考:https://blog.csdn.net/iicy266/article/details/11906509
(18) 引用是否能实现动态绑定,为什么引用可以实现
引用可以实现。从反汇编的角度看,引用的本质是一个由编译器管理的指针。其表现出来的语法特征和其所引用的对象一致。
(19) 深拷贝和浅拷贝的区别(举例说明深拷贝的安全性)
浅拷贝:复制指向对象的指针,两个指针指向同一对象;
深拷贝:复制指向对象的内存,两个指针指向两份同样内容的内存;
安全性:修改内容,浅拷贝影响两个对象,深拷贝影响修改对应对象。
(20) 对象复用的了解,零拷贝的了解
对象池:对象池通过对象复用的方式来避免重复创建对象,它会事先创建一定数量的对象放到池中,当用户需要创建对象的时候,直接从对象池中获取即可,用完对象之后再放回到对象池中,以便复用。
适用性:类的实例可重用。类的实例化过程开销较大。类的实例化的频率较高。
零拷贝:避免CPU将数据从一块存储拷贝到另外一块存储,主要就是利用各种零拷贝技术,避免让CPU做大量的数据拷贝任务,减少不必要的拷贝,或者让别的组件来做这一类简单的数据传输任务,让CPU解脱出来专注于别的任务。这样就可以让系统资源的利用更加有效。
参考:(零拷贝)https://www.jianshu.com/p/fad3339e3448
(21) 介绍C++所有的构造函数
构造函数A(void):用于一般对象的创建。
拷贝构造函数A(const A &a):作为函数的参数,作为函数的返回值,作为对象给另一对象初始化(A a2 = a1;使用的运算符=,实际等效于A a2(a1);)。
赋值构造函数A& operator=(const A
&a):对象给另一对象赋值。
(22) 什么情况下会调用拷贝构造函数
函数的参数为对象。
函数的返回值为对象或对象的引用。
作为对象给另一对象初始化。
参考:https://www.cnblogs.com/wangguchangqing/p/6141743.html
(23) 结构体内存对齐方式和为什么要进行内存对齐?
对齐规则:
1、变量开始地址为其大小的倍数。
2、结构体大小为其最大元素大小的倍数。
3、#pragma pack指定后,结构体大小为min(最大元素大小,指定值)的倍数。
对齐理由:
1、移植原因。某些平台只能在特定地址处取特定数据值。
2、性能原因。访问未对齐的内存,处理器需要额外的访问次数。有的平台从偶地址开始读,如果数据从奇地址开始读,那么需要访问两次。
参考:https://blog.csdn.net/jamesf1982/article/details/4375719
(24) 内存泄露和内存溢出
泄露:申请的内存空间未释放导致。
溢出:1、数组越界。2、缓冲区/栈空间不够用溢出。
(25)遇到coredump要怎么调试
A core dump is a file containing aprocess's address space (memory) when the process terminates unexpectedly. Coredumps may be produced on-demand (such as by a debugger), or automatically upontermination. Core dumps are triggered by the kernel in response to programcrashes, and may be passed to a helper program (such as systemd-coredump) forfurther processing. A core dump is not typically used by an average user, butmay be passed on to developers upon request where it can be invaluable as apost-mortem snapshot of the program's state at the time of the crash,especially if the fault is hard to reliably reproduce.
参考:https://wiki.archlinux.org/index.php/Core_dump
(26) 模板的用法与适用场景
模板是C++泛型编程的基础。Template 。
应用场景:除了参数类型不一样外,其他的内容全部一样(函数体),用模板,而不是每一个类型都写一个函数。
特化:模板的一个独立的定义,其中一个或多个参数被指定为特定的类型。通用模板不能适应所有情况。(特殊情况特殊处理)
参考:(模板特化)https://harttle.land/2015/10/03/cpp-template.html
(27) 成员初始化列表的概念,为什么用成员初始化列表会快一些
两种方式:先构造再复制(default构造->copy assignment),拷贝构造;操作次数不同,因此初始化成员列表更快。
补充:初始化列表顺序为声明顺序,且先基类后派生类。
参考:《effective C++》条款04
(28) 用过C11吗,知道C11新特性吗?(有面试官建议熟悉C11)
1、for(:)
2、auto推断
3、decltype int x= 3; decltype(x) y = x;
4、lambda函数(匿名函数)[capture](para){body};
5、override和final
6、nullptr
7、sizeof(A::member);
参考:
https://my.oschina.net/wangxuanyihaha/blog/183151
https://blog.csdn.net/FX677588/article/details/70157088
(29) C++的调用惯例(简单一点C++函数调用的压栈过程)
(30)C++的四种强制转换
dynamic_cast:
有条件转换,动态类型转换,运行时类型安全检查(转换失败返回NULL):
1、安全的基类和子类之间转换。
2、必须要有虚函数。
3、相同基类不同子类之间的交叉转换。但结果是NULL。
const_cast:
指向常量的指针、指向常量的引用去掉常量属性。注意:对象为指针和引用。
1、常量指针被转化成非常量的指针,并且仍然指向原来的对象;
2、常量引用被转换成非常量的引用,并且仍然指向原来的对象;
3、const_cast一般用于修改指针。如const char *p形式。
static_cast:
1、static_cast 作用和C语言风格强制转换的效果基本一样,由于没有运行时类型检查来保证转换的安全性,所以这类型的强制转换和C语言风格的强制转换都有安全隐患。
2、用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。注意:进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
3、 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性需要开发者来维护。
4、 static_cast不能转换掉原有类型的const、volatile、或者 __unaligned属性。(前两种可以使用const_cast 来去除)
5、在c++ primer 中说道:c++的任何的隐式转换都是使用 static_cast 来实现。
reinterpret_cast:
reinterpret_cast操作符代替了大多数其它C风格类型转换的使用。reinterpret_cast将指针转换为其它指针类型、将数字转换为指针或将指针转换为数字。
参考:
https://blog.csdn.net/ydar95/article/details/69822540
https://www.cnblogs.com/balingybj/p/4771141.html
(31)STL源码中的hash表的实现
存{key, value}:
1、BucketNum = func(hash(key))
2、value放入Mem[BucketNum]
取{key, value}:
1、BucketNum = func(hash(key))
2、在Mem[BucketNum]中查找value
参考:
https://blog.csdn.net/u010025211/article/details/46653519
(32) STL中unordered_map和map的区别
使用map情况:需要有序的数据。
使用unordered_map:不要求数据的排序。
参考:
(unordered_map vs map) https://www.geeksforgeeks.org/map-vs-unordered_map-c/
(hash table vs BST) https://www.geeksforgeeks.org/advantages-of-bst-over-hash-table/
(33) STL中vector的实现
三指针:_Myfirst, _Mylast, _Myend
参考:https://blog.csdn.net/u012658346/article/details/50725933
(34) vector使用的注意点及其原因,频繁对vector调用push_back()对性能的影响和原因。
vector的capacity不够用时,会重新分配内存空间,并且将原来的数据复制到新的空间中,该过程开销大。
参考:https://blog.csdn.net/xi_niuniu/article/details/47060453
总参考:
(C++面试题整理)https://www.jianshu.com/p/9aae0e5dc21a