1. 指针和引用的区别html
2. 堆和栈的区别linux
3. malloc/free与new/delete异同点,new和delete是如何实现的,new 与 malloc的异同处c++
相同点:程序员
不一样点:正则表达式
malloc/free是C++/C语言的标准库文件,new/delete是C++的运算符;算法
对非内部数据对象,malloc/free没法知足动态对象要求。对象在建立时要自动执行构造函数,对象消亡以前要自动执行析构函数,而malloc/free是库函数,不是运算符,故不在编译器控制权限以内,不可以将执行构造函数和析构函数强加于malloc/free身上。而因为new/delete是C++语言,可以完成动态内存分配和初始化工做,并可以完成清理与释放内存工做,即可以自动执行构造函数和析构函数;编程
malloc分配内存空间前须要计算分配内存大小;而new可以自动分配内存空间;设计模式
malloc是底层函数,其函数返回值类型为void *;而new运算符调用无参构造函数,故返回值为对应对象的指针;数组
malloc函数类型不是安全的,编译器不对其进行类型转换、类型安全的相关检查。malloc申请空间后,不会对其初始化,要单独初始化;而new类型是安全的,由于它内置了sizeof、类型转换和类型安全检查功能,且在建立对象时,就完成了初始化工做,通常初始化调用无参构造函数;缓存
operator new对应于malloc,且operator new能够重载,能够自定义内存分配策略,甚至不作内存分配,甚至分配到非内存设备上;但malloc不能。
free只进行释放空间;而delete则释放空间的同时调用析构函数。此外delete使用是注意释放数组的方法为delete []数组名。
4. C和C++的区别?面向对象编程的特色?
"面向过程的模型",按这种模型编写的程序以一系列的线性步骤(代码)为特征,可被理解为做用于数据的代码.
"面向对象的模型",按这种模型编写的程序围绕着程序的数据(对象)和针对该对象而严格定义的接口来组织程序,它的特色是数据控制代码的访问.经过把控制权转移到数据上,面向对象的模型在组织方式上有:抽象,封装,继承和多态的好处.
(数据抽象)如一棵树的高度、一本书的页数等。不过,有些特征并不能用这样的办法来表示,(过程抽象)如一只狗会跑。用数据来表示这个跑的动做是作不到的,由于这是一个动做过程,而不是一种状态。
多态指一样的方法在不一样的对象中有不一样的表现方式。多态实现的方法,
静态多态----函数重载 泛型编程
动态多态----虚函数
5. Struct和class的区别
当struct和class中都定义了构造函数,就不能使用大括号对其进行初始化
若没有定义构造函数,struct可使用{ }进行初始化,而只有当class的全部数据成员及函数为public时,可使用{ }进行初始化
因此struct更适合当作是一个数据结构的实现体,class更适合当作是一个对象的实现体。
6. define 和const的区别(编译阶段、安全性、内存占用等)
define宏是在预处理阶段展开。
const常量是编译运行阶段使用。
define宏没有类型,不作任何类型检查,仅仅是展开。
const常量有具体的类型,在编译阶段会执行类型检查。
define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。
const常量会在内存中分配(能够是堆中也能够是栈中)。
#define PI 3.14159 //常量宏
const doulbe Pi=3.14159; //此时并未将Pi放入ROM中 ......
double i=Pi; //此时为Pi分配内存,之后再也不分配!
double I=PI; //编译期间进行宏替换,分配内存
double j=Pi; //没有内存分配
double J=PI; //再进行宏替换,又一次分配内存!
const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define同样给出的是当即数,因此,const定义的常量在程序运行过程当中只有一份拷贝,而 #define定义的常量在内存中有若干个拷贝。
编译器一般不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操做,使得它的效率也很高
7. C/C++中static和const关键字的做用总结
(1)函数体内 static 变量的做用范围为该函数体,不一样于auto 变量,该变量的内存只被分配一次,所以其值在下次调用时仍维持上次的值;
(2)在模块内的 static 全局变量能够被模块内所用函数访问,但不能被模块外其它函数访问;
(3)在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
(4)在类中的 static 成员变量属于整个类所拥有,对类的全部对象只有一份拷贝;
(5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,于是只能访问类的 static 成员变量。
(1)欲阻止一个变量被改变,可使用 const 关键字。在定义该 const 变量时,一般须要对它进行初始化,由于之后就没有机会再去改变它了;
(2)对指针来讲,能够指定指针自己为 const,也能够指定指针所指的数据为 const,或两者同时指定为 const;
(3)在一个函数声明中,const 能够修饰形参,代表它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为 const 类型,则代表其是一个常函数,不能修改类的成员变量;
8. C++的顶层const和底层const
顶层const表示指针自己是个常量,底层const表示指针所指的对象是一个常量。
Int *const p1 = &i; 不能改变P1的值,这是一个顶层const
Const int *p2 = &i; 容许改变P2的值,这是一个底层const
用实参初始化形参时会忽略到顶层const。换句话说,形参的顶层const被忽略掉了
9. final和override关键字
class Base
{
virtual void foo();
};
class A : Base
{
void foo() final; // foo 被override而且是最后一个override,在其子类中不能够重写
void bar() final; // Error: 父类中没有 bar虚函数能够被重写或final
};
class B final : A // 指明B是不能够被继承的
{
void foo() override; // Error: 在A中已经被final了
};
class C : B // Error: B is final
{
};
class A
{
virtual void foo();
};
class B :A
{
virtual void foo() override; //OK
virtual void f00() override; //Error ,由于基类中没有void f00()函数
};
10. 拷贝初始化和直接初始化,初始化和赋值的区别
http://www.javashuo.com/article/p-ewwxvnpf-hd.html
https://blog.csdn.net/ljianhui/article/details/9245661
https://blog.csdn.net/splendid7/article/details/81537706
http://www.javashuo.com/article/p-wgwpsesw-ez.html(移动构造函数)
(1)什么是拷贝初始化(也称为复制初始化):将一个已有的对象拷贝到正在建立的对象,若是须要的话还须要进行类型转换。拷贝初始化发生在下列状况:
1.使用赋值运算符定义变量
2.将对象做为实参传递给一个非引用类型的形参
3.将一个返回类型为非引用类型的函数返回一个对象
4.用花括号列表初始化一个数组中的元素或一个聚合类中的成员
(2)什么是直接初始化:在对象初始化时,经过括号给对象提供必定的参数,而且要求编译器使用普通的函数匹配来选择与咱们提供的参数最匹配的构造函数
ClassTest ct2 = "ab";//复制初始化
ClassTest ct2("ab");//直接初始化
11. extern "C"的用法
为了可以正确的在C++代码中调用C语言的代码;在程序中加上extern "C"后,至关于告诉编译器这部分代码是C语言写的,所以要按照C语言进行编译,而不是C++;
12. 模板函数和模板类的特例化
Typename关键字 告诉编译把一个特殊的名字解释成一个类型,类模板时能够换成class
template<typename T> void swap(T& t1, T& t2)
{
T tmpT; tmpT = t1;
t1 = t2; t2 = tmpT;
}
#include <stdio.h> #include "method.h"
int main()
{
//模板方法 int num1 = 1, num2 = 2;
swap<int>(num1, num2);
printf("num1:%d, num2:%d\n", num1, num2);
return 0;
}
13. C++的STL源码
STL是一个c++里面很是强大的库,c11引进的,里面封装例如容器,泛型算法等
14. STL源码中的hashtable的实现
hash_table是STL中hash_map 和 hash_set 的内部数据结构,hash_table的插入/删除/查找的时间复杂度都为O(1),是查找速度最快的一种数据结构,可是hash_table中的数据是无序的,通常也只有在数据不须要排序,只须要知足快速查找/插入/删除的时候使用hash_table。hash_table的扩展是将原hash_table中的数据摘下来插入到一个临时的hash_table中,由于每一个桶都使用list来实现的,所以插入删除都不存在内存copy,因此也是很高效的,最后再将临时hash_table和原来的hash_table(此时已经为空)交换。
15. STL中unordered_map和map的区别和应用场景
map
优势:
● 有序性,这是map结构最大的优势,其元素的有序性在不少应用中都会简化不少的操做
● 红黑树,内部实现一个红黑书使得map的不少操做在lgn的时间复杂度下就能够实现,所以效率很是的高
缺点:
● 空间占用率高,由于map内部实现了红黑树,虽然提升了运行效率,可是由于每个节点都须要额外保存父节点,孩子节点以及红/黑性质,使得每个节点都占用大量的空间
● 适用处,对于那些有顺序要求的问题,用map会更高效一些
unordered_map
优势:
● 由于内部实现了哈希表,所以其查找速度很是的快
缺点:
● 哈希表的创建比较耗费时间
● 适用处,对于查找问题,unordered_map会更加高效一些,所以遇到查找问题,常会考虑一下用unordered_map
16. STL中vector的实现
注意两个点:
1.vector有备用空间,当备用空间不够的时候,会从新开辟原空间两倍的空间进行重写分配。
2.vector支持随机的存取,可是最好是选择从末尾插入,由于从中间插入会致使元素的移动,带来了性能的开销。
17. STL容器的几种迭代器以及对应的容器(输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机访问迭代器)
19. vector使用的注意点及其缘由,频繁对vector调用push_back()对性能的影响和缘由。
20. C++中的重载和重写的区别
21. C++内存管理,内存池技术(热门问题),与csapp中几种内存分配方式对比学习加深理解???
c++的内存管理延续c语言的内存管理,可是也增长了其余的,例如智能指针,除了常见的堆栈的内存管理以外,c++支持智能指针,智能指针的对象进行赋值拷贝等操做的时候,每一个智能指针都有一个关联的计数器,该计数器记录共享该对象的指针个数,当最后一个指针被销毁的时候,计数器为0,会自动调用析构函数来销毁函数。
22. 介绍面向对象的三大特性,而且举例说明每个
继承,多态,封装。
23. C++多态的实现
多态经过覆盖和重载来完成。
24. C++虚函数相关(虚函数表,虚函数指针),虚函数的实现原理(包括单一继承,多重继承等)(拓展问题:为何基类指针指向派生类对象时能够调用派生类成员函数,基类的虚函数存放在内存的什么区,虚函数表指针vptr的初始化时间)
虚函数分为两种,纯虚函数和虚函数,纯虚函数适用于抽象基类,不须要定义,相似一种接口,是多态的典型处理方式。
一个类若是定义了虚函数,那么编译器会自动为它加上一个虚函数表,并提供一个指向虚函数表的指针,子类经过继承,能够覆盖父类的虚函数,当用户调用虚函数的时候,会调用指针,去虚函数表中找匹配的虚函数,若是当前对象有覆盖的虚函数,则去执行覆盖的虚函数,不然执行父类的虚函数。
26. this指针
27. 析构函数通常写成虚函数的缘由
delete父类指针时,能够调用对象真正的析构函数。不然可能会出错,如子类中有指针类型,而且申请了内存,这时就会形成内存泄漏。
28. 构造函数、拷贝构造函数和赋值操做符的区别
30. 构造函数为何通常不定义为虚函数
三个缘由:
1.虚函数的做用是什么?是实现部分或默认的功能,并且该功能能够被子类所修改。若是父类的构造函数设置成虚函数,那么子类的构造函数会直接覆盖掉父类的构造函数。而父类的构造函数就失去了一些初始化的功能。这与子类的构造须要先完成父类的构造的流程相违背了。而这个后果会至关严重。
2.虚函数的调用是须要经过“虚函数表”来进行的,而虚函数表也须要在对象实例化以后才可以进行调用。在构造对象的过程当中,尚未为“虚函数表”分配内存。因此,这个调用也是违背先实例化后调用的准则。
3.虚函数的调用是由父类指针进行完成的,而对象的构造则是由编译器完成的,因为在建立一个对象的过程当中,涉及到资源的建立,类型的肯定,而这些是没法在运行过程当中肯定的,须要在编译的过程当中就肯定下来。而多态是在运行过程当中体现出来的,因此是不可以经过虚函数来建立构造函数的,与实例化的次序不一样也有关系。
31. 构造函数的几种关键字(default delete 0)
32. 构造函数或者析构函数中调用虚函数会怎样
结果上来看和调用普通函数无异。即基类调用基类的虚函数,派生类调用派生类的虚函数。
不要在构造函数和析构函数内调用虚函数。假如你定义了一个对象,首先调用的是基类的构造函数,而此时基类构造函数内的虚函数不是调用的是此对象类的对应函数,是基类的对应函数,与咱们的预期不同。
34. 静态类型和动态类型,静态绑定和动态绑定的介绍
一、对象的静态类型(static type):就是它在程序中被声明时所采用的类型(或理解为类型指针或引用的字面类型),在编译期肯定;
二、对象的动态类型(dynamic type):是指“目前所指对象的类型”(或理解为类型指针或引用的实际类型),在运行期肯定;
这两个概念通常发生在基类和派生类之间。
三、静态绑定(statically bound):又名前期绑定(eraly binding),绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;
四、动态绑定(dynamically bound):又名后期绑定(late binding),绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;
好比常见的,virtual函数是动态绑定,non-virtual函数是静态绑定,缺省参数值也是静态绑定。当缺省参数和虚函数一块儿出现的时候状况有点复杂,极易出错。咱们知道,虚函数是动态绑定的,可是为了执行效率,缺省参数是静态绑定的
http://www.javashuo.com/article/p-fihsqwfh-kc.html
35. 引用是否能实现动态绑定,为何引用能够实现
能够实现,由于动态绑定是发生在程序运行阶段的,c++中动态绑定是经过对基类的引用或者指针调用虚函数时发生。
引用和指针的静态类型和动态类型能够不同。
36. 深拷贝和浅拷贝的区别(举例说明深拷贝的安全性)
37. 对象复用的了解,零拷贝的了解
38. 介绍C++全部的构造函数
默认,普通,拷贝,赋值:默认无参,普通有参数,其实还有this指针,拷贝就是const class &A,赋值构造函数是对赋值符号的重载,若是有动态生成的对象的话,那么它作赋值,原先的动态空间必定要被释放掉,而后再赋上新的值,因此必须得本身重载=,实现delete。
39. 什么状况下会调用拷贝构造函数(三种状况)
一个对象去初始化,值传递类,返回值是类
40. 结构体内存对齐方式和为何要进行内存对齐?
保证计算机能够一次读出。
结构体不像数组,结构体中能够存放不一样类型的数据,它的大小也不是简单的各个数据成员大小之和,限于读取内存的要求,而是每一个成员在内存中的存储都要按照必定偏移量来存储,根据类型的不一样,每一个成员都要按照必定的对齐数进行对齐存储,最后整个结构体的大小也要按照必定的对齐数进行对齐。
一样的结构体,里面的数据排放顺序不同,最后占用的内存也不同大
//平台VS2013下(默认对齐数为8)
//练习一
struct S1
{
char c1;
int i;
short s2;
};
printf("%d\n", sizeof(struct S1));//12
//练习二
struct S2
{
char c1;
short s2;
int i;
};
printf("%d\n", sizeof(struct S2));//8
41. 内存泄露的定义,如何检测与避免?
42. 手写智能指针的实现(shared_ptr和weak_ptr实现的区别)
shared_ptr使用引用计数,每个shared_ptr的拷贝都指向相同的内存。在最后一个shared_ptr析构的时候,内存才会被释放。
注意事项:
1.不要用一个原始指针初始化多个shared_ptr。
2.不要再函数实参中建立shared_ptr,在调用函数以前先定义以及初始化它。
3.不要将this指针做为shared_ptr返回出来。
4.要避免循环引用。
<1>Unique_ptr是一个独占的智能指针,他不容许其余的智能指针共享其内部的指针,不容许经过赋值将一个unique_ptr赋值给另一个 unique_ptr。
<2>unique_ptr不容许复制,但能够经过函数返回给其余的unique_ptr,还能够经过std::move来转移到其余的unique_ptr,这样它自己就再也不 拥有原来指针的全部权了。
<3>若是但愿只有一个智能指针管理资源或管理数组就用unique_ptr,若是但愿多个智能指针管理同一个资源就用shared_ptr。
弱引用的智能指针weak_ptr是用来监视shared_ptr的,不会使引用计数加一,它无论理shared_ptr内部的指针,主要是为了监视shared_ptr的生命周期,更像是shared_ptr的一个助手。 weak_ptr没有重载运算符*和->,由于它不共享指针,不能操做资源,主要是为了经过shared_ptr得到资源的监测权,它的构造不会增长引用计数,它的析构不会减小引用计数,纯粹只是做为一个旁观者来监视shared_ptr中关连的资源是否存在。 weak_ptr还能够用来返回this指针和解决循环引用的问题。
44. 遇到coredump要怎么调试???
45. 内存检查工具的了解???
linux可使用开源的Valgrind工具包,包含多个工具:Memcheck经常使用语检测malloc和new这类的问题,callgrind用来检查函数调用,cachegrind缓存使用,helgrind多线程程序中的竞争。除了valgrind还能够用mtrace这些工具
46. 模板的用法与适用场景
代码可重用,泛型编程,在不知道参数类型下,函数模板和类模板
47. 成员初始化列表的概念,为何用成员初始化列表会快一些(性能优点)?
使用初始化list这样能够直接调用成员的构造函数,不用去赋值产生临时变量,因此更快。若是不用,可能会去调用成员的构造函数和赋值构造函数(多出来的)。
48. 用过C++ 11吗,知道C++ 11哪些新特性?
Ambda表达式, 智能指针 移动,auto,范围for,decltype,array,forward_list,tuple(元组),正则表达式库,随机数库,bitset运算
http://www.javashuo.com/article/p-pqeqhgcx-hm.html
https://blog.csdn.net/fjb2080/article/details/15809097
tuple<const char*, int>tp = make_tuple(sendPack,nSendSize); //构造一个tuple
这个tuple等价于一个结构体
struct A
{
char* p;
int len;
};
用tuple<const char*, int>tp就能够不用建立这个结构体了,而做用是同样的,是否是更简洁直观了。还有一种方法也能够建立元组,用std::tie,它会建立一个元组的左值引用。
auto tp = return std::tie(1, "aa", 2);
//tp的类型实际是:std::tuple<int&,string&, int&>
再看看如何获取它的值:
const char* data = tp.get<0>(); //获取第一个值
int len = tp.get<1>(); //获取第二个值
c. emplace_back( “666-556695” , 25 , 52.36);///直接使用Sales_data的三个参数就能够了
c.push_back(“666-556695” , 25 , 52.36); //错误,.push_back不接受3个参数
c.push_back( Sales_data(“666-556695” , 25 , 52.36) ); //正确,建立一个临时对象
49. C++的调用惯例(简单一点C++函数调用的压栈过程)
函数调用你们都不陌生,调用者向被调用者传递一些参数,而后执行被调用者的代码,最后被调用者向调用者返回结果。
对于程序,编译器会对其分配一段内存,在逻辑上能够分为代码段,数据段,堆,栈
代码段:保存程序文本,指令指针EIP就是指向代码段,可读可执行不可写
数据段:保存初始化的全局变量和静态变量,可读可写不可执行
BSS(静态内存分配):未初始化的全局变量和静态变量
堆(Heap):动态分配内存,向地址增大的方向增加,可读可写可执行
栈(Stack):存放局部变量,函数参数,当前状态,函数调用信息等,向地址减少的方向增加,很是很是重要,可读可写可执行
程序开始,从main开始,首先将参数压入栈,而后压入函数返回地址,进行函数调用,经过跳转指定进入函数,将函数内部的变量去堆栈上开辟空间,执行函数功能,执行完成,取回函数返回地址,进行下一个函数。
50. C++的四种强制转换
int* aas = new int[1] ;
aas[0] = 1;
int b = reinterpret_cast<int>(aas); ////b中的值为aas 指针的地址
const int constA = 10;
const int *pConstA = &constA;
int* b = const_cast<int*>(pConstA);
*b = *b + 1;
printf("%d ,%d",constA,*b);/////10 11 能够发现constA中的值并无发生变化,由于constA是一个常量,若constA没有const的修饰,最后的结果将是11 11
对于“向上转换”(即派生类指针或引用类型转换为其基类类型),不管是指针仍是引用向上转换都是安全地。
对于“向下转型”有两种状况:
一种是基类指针所指对象是派生类类型的,这种转换是安全的;另外一种是基类指针所指对象为基类类型,在这种状况下dynamic_cast在运行时作检查,转换失败,返回结果为0;
53. volatile关键字
volatile关键字是一种限定符用来声明一个对象在程序中能够被语句外的东西修改,好比操做系统、硬件或并发执行线程。
遇到该关键字,编译器再也不对该变量的代码进行优化,再也不从寄存器中读取变量的值,而是直接从它所在的内存中读取值,即便它前面的指令刚刚从该处读取过数据。并且读取的数据马上被保存。
通常说来,volatile用在以下的几个地方:
(1)、中断服务程序中修改的供其它程序检测的变量须要加volatile;
(2)、多任务环境下各任务间共享的标志应该加volatile;
(3)、存储器映射的硬件寄存器一般也要加volatile说明,由于每次对它的读写均可能有不一样
54. 优化程序的几种方法
http://www.javashuo.com/article/p-hvuvuhyo-hp.html
一、 选择合适的算法和数据结构
二、 使用尽可能小的数据类型
三、 减小运算的强度
四、 结构体成员的布局
五、 循环优化
六、 提升CPU的并行性(使用并行代码,把长的代码分解成短的)
七、 循环不变计算(不使用的循环变量,放到外面)
八、 函数优化(内联函数)
55. public,protected和private访问权限和继承
无论哪一种继承方式,父类的私有成员都不能够访问,只有间接的经过公有成员才能获取到私有成员的值。
protected存在的意义是当我不想向外部暴露某个函数或者成员变量,可是我又想让派生类知道和访问这个成员,就将其用protected标志。
将普通函数添加为友元函数,能够实如今该函数内调用类内的私有与保护成员
在类外定义对象,该对象只能访问类内的公有成员二不能访问其类内的私有与保护成员
类内的成员可使用类内的全部成员,继承类可使用父类的保护成员函数
56. decltype()和auto
Const int c = 0,&b = c;
Decltype(c) x =3;//x是const int 类型
Decltype(b) y = x;//y是const int&类型;因此定义y时必须初始化
使用auto也能在一条语句中声明多个变量,由于一条语句只能有一个基本的数据类型。
Auto定义的变量必须有初值,这样才能推导出类型是什么
57. inline和宏定义的区别
(1)内联函数在编译时展开,宏在预编译时展开;
(2)内联函数直接嵌入到目标代码中,宏是简单的作文本替换;
(3)内联函数有类型检测、语法判断等功能,而宏没有;
(4)inline函数是函数,宏不是;
(5)宏定义时要注意书写(参数要括起来)不然容易出现歧义,内联函数不会产生歧义;
59. 调试程序的方法:
概括法、演绎法、测试法,回溯法,暴力法。设置断点,进入调试,单步运行,进入函数,查看变量。linux gbk,win windbg
60. 类型别名
Typedef double wages; //wages是double的同义词
Typedef wages base, *p; // base是double的同义词,p是double*的同义词