C++面试常见问题汇总

 

1.      指针和引用的区别html

    1. 能够有多级指针,可是没有多级引用
    2. 指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。
    3. 引用不能够为空,当被建立的时候,必须初始化,而指针能够是空值,能够在任什么时候候被初始化。
    4. 能够有const指针,可是没有const引用;
    5. 指针的值在初始化后能够改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了
    6. 指针和引用的自增(++)运算意义不同;

2.      堆和栈的区别linux

    1. 堆空间的内存是动态分配的,通常存放对象,而且须要手动释放内存。栈空间的内存是由系统自动分配,通常存放局部变量,好比对象的地址等值,不须要程序员对这块内存进行管理,
    2. 栈是运行时的单位,而堆是存储的单位。堆中的共享常量和缓存 。栈解决程序的运行问题,即程序如何执行,或者说如何处理数据;堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
    3. 栈由于是运行单位,所以里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等等;而堆只负责存储对象信息。

3.      malloc/free与new/delete异同点,new和delete是如何实现的,new 与 malloc的异同处c++

相同点:程序员

  1. malloc/free与new/delete均可以用于申请动态内存和释放内存,他们申请的空间都在堆上分配。

不一样点:正则表达式

  • 操做对象不一样

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 []数组名。

  • new先调用malloc,后调用构造函数,delete先调用析构后调用free;

4.      C和C++的区别?面向对象编程的特色?

"面向过程的模型",按这种模型编写的程序以一系列的线性步骤(代码)为特征,可被理解为做用于数据的代码.

"面向对象的模型",按这种模型编写的程序围绕着程序的数据(对象)和针对该对象而严格定义的接口来组织程序,它的特色是数据控制代码的访问.经过把控制权转移到数据上,面向对象的模型在组织方式上有:抽象,封装,继承和多态的好处.

 (数据抽象)如一棵树的高度、一本书的页数等。不过,有些特征并不能用这样的办法来表示,(过程抽象)如一只狗会跑。用数据来表示这个跑的动做是作不到的,由于这是一个动做过程,而不是一种状态。

多态指一样的方法在不一样的对象中有不一样的表现方式。多态实现的方法,

静态多态----函数重载  泛型编程

动态多态----虚函数

5.      Struct和class的区别

    1. 默认的继承与访问权限:struct默认是公有继承(public),class默认是私有继承(private)
    2. 在C++中对struct的功能进行了扩展,struct能够被继承,能够包含成员函数,也能够实现多态,当用大括号对其进行初始化须要注意:

当struct和class中都定义了构造函数,就不能使用大括号对其进行初始化

若没有定义构造函数,struct可使用{ }进行初始化,而只有当class的全部数据成员及函数为public时,可使用{ }进行初始化

因此struct更适合当作是一个数据结构的实现体,class更适合当作是一个对象的实现体。

6.      define 和const的区别(编译阶段、安全性、内存占用等)

    1.  编译器处理方式不一样

define宏是在预处理阶段展开。

const常量是编译运行阶段使用。

  1. 类型和安全检查不一样

  define宏没有类型,不作任何类型检查,仅仅是展开。

  const常量有具体的类型,在编译阶段会执行类型检查。

  1. 存储方式不一样

  define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。

  const常量会在内存中分配(能够是堆中也能够是栈中)。

  1. 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定义的常量在内存中有若干个拷贝。

  1. 提升了效率。

编译器一般不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它成为一个编译期间的常量,没有了存储与读内存的操做,使得它的效率也很高

7.      C/C++中static和const关键字的做用总结

  • static 关键字至少有下列 n 个做用:

  (1)函数体内 static 变量的做用范围为该函数体,不一样于auto 变量,该变量的内存只被分配一次,所以其值在下次调用时仍维持上次的值;

  (2)在模块内的 static 全局变量能够被模块内所用函数访问,但不能被模块外其它函数访问;

  (3)在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

  (4)在类中的 static 成员变量属于整个类所拥有,对类的全部对象只有一份拷贝;

(5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,于是只能访问类的 static 成员变量。

  • const 关键字至少有下列 n 个做用

(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关键字

  • final限定某个类不能被继承或某个虚函数不能被重写。若是修饰函数只能修饰虚函数,且要写到类或函数后面。

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

{

};

  • override关键字保证了派生类中声明重写的函数与基类虚函数有相同的签名,可避免一些拼写错误,如加了此关键字但基类中并不存在相同的函数就会报错,也能够防止把原本想重写的虚函数声明成了重载。同时在阅读代码时若是看到函数声明后加了此关键字就能立马知道此函数是重写了基类虚函数。保证重写虚函数的正确性的同时也提升了代码可读性。

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");//直接初始化
  • 直接初始化和拷贝初始化效率基本同样,由于在底层的实现基本同样,因此将拷贝初始化改成直接初始化效率提升不大
  • 拷贝初始化何时使用拷贝构造函数:???
  1. 赋值表达式右边是一个对象
  2. 直接初始化时,括号内的参数是一个对象
  3. 用花括号列表初始化一个数组中的元素或一个聚合类中的成员
  4. 将一个返回类型为引用类型的函数返回一个对象
  5. 形参为非引用类型的函数,其中是将实参拷贝到临时对象
  • 何时使用到拷贝赋值运算符:???
  1. 赋值表达式右边是一个左值对象(若是须要,能够调用构造函数类型转换,生成一个临时对象)
  2. 当赋值表达式右边是一个右值对象,且没有定义移动赋值运算符函数
  • 何时使用移动赋值运算符:???
  1. 当赋值表达式右边是一个右值对象,且定义了移动赋值运算符函数

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是一种映射,这种映射是有序的,底层是使用红黑树来完成的,数据经过键值才存储,键是惟一的。
  • unordered_map,是一种无序的,底层是经过hash表来完成的。unordered库使用“桶”来存储元素,散列值相同的被存储在一个桶里。当散列容器中有大量数据时,同一个桶里的数据也会增多,形成访问冲突,下降性能。为了提升散列容器的性能,unordered库会在插入元素是自动增长桶的数量,不须要用户指定。每一个桶都是用list来完成的。

map

优势:

● 有序性,这是map结构最大的优势,其元素的有序性在不少应用中都会简化不少的操做

● 红黑树,内部实现一个红黑书使得map的不少操做在lgn的时间复杂度下就能够实现,所以效率很是的高

缺点:

● 空间占用率高,由于map内部实现了红黑树,虽然提升了运行效率,可是由于每个节点都须要额外保存父节点,孩子节点以及红/黑性质,使得每个节点都占用大量的空间

● 适用处,对于那些有顺序要求的问题,用map会更高效一些

unordered_map

优势

● 由于内部实现了哈希表,所以其查找速度很是的快

缺点

● 哈希表的创建比较耗费时间

● 适用处,对于查找问题,unordered_map会更加高效一些,所以遇到查找问题,常会考虑一下用unordered_map

16.  STL中vector的实现

注意两个点:

1.vector有备用空间,当备用空间不够的时候,会从新开辟原空间两倍的空间进行重写分配。

2.vector支持随机的存取,可是最好是选择从末尾插入,由于从中间插入会致使元素的移动,带来了性能的开销。

17.  STL容器的几种迭代器以及对应的容器(输入迭代器,输出迭代器,前向迭代器,双向迭代器,随机访问迭代器)

  • 顺序容器:vector,deque是随机访问迭代器;list是双向迭代器
  • 容器适配器:stack,queue,priority_queue没有迭代器
  • 关联容器:set,map,multiset,multimap是双向迭代器
  • unordered_set,unordered_map,unordered_multiset,unordered_multimap是前向迭代器
  1. STL中的traits技法???

19.  vector使用的注意点及其缘由,频繁对vector调用push_back()对性能的影响和缘由。

    1. 在一个vector的尾部以外的任何位置添加元素,都须要从新移动元素。
    2. 并且,向一个vector添加元素可能引发整个对象存储空间的从新分配。从新分配一个对象的存储空间须要分配新的内存,并将元素从旧的空间移到新的空间(由于vector在内存中的地址是连续的,因此加入一个可能形成地址不够)

20.  C++中的重载和重写的区别

  • 重定义发生在继承中,从新定义了一个函数;重写(虚函数多态关键):与重定义差很少,只是重写的是虚函数;
  • 重载是指发生在同一个类中,名称相同,可是参数个数或者类型不一样。

21.  C++内存管理,内存池技术(热门问题),与csapp中几种内存分配方式对比学习加深理解???

c++的内存管理延续c语言的内存管理,可是也增长了其余的,例如智能指针,除了常见的堆栈的内存管理以外,c++支持智能指针,智能指针的对象进行赋值拷贝等操做的时候,每一个智能指针都有一个关联的计数器,该计数器记录共享该对象的指针个数,当最后一个指针被销毁的时候,计数器为0,会自动调用析构函数来销毁函数。

22.  介绍面向对象的三大特性,而且举例说明每个

继承,多态,封装。

  • 继承:某些类似的特性,能够从一个类继承到另外一个类,相似生活中的继承,例如有个全部的汽车都有4个轮子,那么咱们在父类中定义4个轮子,经过继承得到4个轮子的功能,不用再类里面再去定义这4个轮子的功能。。
  • 多态:多态和虚函数。多态指的相同的功能,不一样的状态,多态在面向对象c++里面是经过重载和覆盖来完成的,覆盖在c++里面经过虚函数来完成的。例如鸭子的例子,全部的鸭子都有颜色,咱们能够将这个颜色设置成为一个虚函数,经过继承子类对虚函数进行覆盖,不一样子类中有各自的颜色,也就是有各自不一样的鸭子颜色,这就是多态的典型表现之一
  • 封装:将不少有类似特性的内容封装在一个类中,例如学生的成绩学号、课程这些能够封装在同一个类中。

23.  C++多态的实现

多态经过覆盖和重载来完成。

24.  C++虚函数相关(虚函数表,虚函数指针),虚函数的实现原理(包括单一继承,多重继承等)(拓展问题:为何基类指针指向派生类对象时能够调用派生类成员函数,基类的虚函数存放在内存的什么区,虚函数表指针vptr的初始化时间)

虚函数分为两种,纯虚函数和虚函数,纯虚函数适用于抽象基类,不须要定义,相似一种接口,是多态的典型处理方式。

一个类若是定义了虚函数,那么编译器会自动为它加上一个虚函数表,并提供一个指向虚函数表的指针,子类经过继承,能够覆盖父类的虚函数,当用户调用虚函数的时候,会调用指针,去虚函数表中找匹配的虚函数,若是当前对象有覆盖的虚函数,则去执行覆盖的虚函数,不然执行父类的虚函数。

 

 

26.  this指针

    1. 一个对象的this指针并非对象自己的一部分,不会影响sizeof(对象)的结果。
    2. 全局函数,静态函数都不能使用this。静态函数表示了整个类范围意义上的信息,而this指针却实实在在的对应一个对象
    3. this在成员函数的开始执行前构造的,在成员的执行结束后清除
    4. this指针只能在一个类的成员函数中调用,它表示当前对象的地址

27.  析构函数通常写成虚函数的缘由

delete父类指针时,能够调用对象真正的析构函数。不然可能会出错,如子类中有指针类型,而且申请了内存,这时就会形成内存泄漏。

28.  构造函数、拷贝构造函数和赋值操做符的区别

    1. 构造函数:对象不存在,没用别的对象初始化
    2. 拷贝构造函数:对象不存在,用别的对象初始化
    3. 赋值运算符:对象存在,用别的对象给它赋值

30.  构造函数为何通常不定义为虚函数

  • 对象都尚未实例化,怎么去找虚表

三个缘由

1.虚函数的做用是什么?是实现部分或默认的功能,并且该功能能够被子类所修改。若是父类的构造函数设置成虚函数,那么子类的构造函数会直接覆盖掉父类的构造函数。而父类的构造函数就失去了一些初始化的功能。这与子类的构造须要先完成父类的构造的流程相违背了。而这个后果会至关严重。

2.虚函数的调用是须要经过“虚函数表”来进行的,而虚函数表也须要在对象实例化以后才可以进行调用。在构造对象的过程当中,尚未为“虚函数表”分配内存。因此,这个调用也是违背先实例化后调用的准则。

3.虚函数的调用是由父类指针进行完成的,而对象的构造则是由编译器完成的,因为在建立一个对象的过程当中,涉及到资源的建立,类型的肯定,而这些是没法在运行过程当中肯定的,须要在编译的过程当中就肯定下来。而多态是在运行过程当中体现出来的,因此是不可以经过虚函数来建立构造函数的,与实例化的次序不一样也有关系。

31.  构造函数的几种关键字(default delete 0)

    1. = default:将拷贝控制成员定义为=default显式要求编译器生成合成的版本
    2. = delete:将拷贝构造函数和拷贝赋值运算符定义删除的函数,阻止拷贝(析构函数不能是删除的函数 C++Primer P450)
    3. = 0:将虚函数定义为纯虚函数(纯虚函数无需定义,= 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.  对象复用的了解,零拷贝的了解

  • 对象复用指得是设计模式,对象能够采用不一样的设计模式达到复用的目的,最多见的就是继承和组合模式了
  • 零拷贝:零拷贝主要的任务就是避免CPU将数据从一块存储拷贝到另一块存储,主要就是利用各类零拷贝技术,避免让CPU作大量的数据拷贝任务,减小没必要要的拷贝,或者让别的组件来作这一类简单的数据传输任务,让CPU解脱出来专一于别的任务。这样就可让系统资源的利用更加有效。

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.  内存泄露的定义,如何检测与避免?

  • 内存泄漏(memory leak)是指因为疏忽或错误形成了程序未能释放掉再也不使用的内存的状况。内存泄漏并不是指内存在物理上的消失,而是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制,于是形成了内存的浪费。
  • 堆内存泄漏 (Heap leak)。对内存指的是程序运行中根据须要分配经过malloc,realloc new等从堆中分配的一块内存,再是完成后必须经过调用对应的 free或者delete 删掉。若是程序的设计的错误致使这部份内存没有被释放,那么此后这块内存将不会被使用,就会产生Heap Leak.
  • 系统资源泄露(Resource Leak).主要指程序使用系统分配的资源好比 Bitmap,handle ,SOCKET等没有使用相应的函数释放掉,致使系统资源的浪费,严重可致使系统效能下降,系统运行不稳定。
  • 解决内存泄漏最有效的办法就是使用智能指针

42.  手写智能指针的实现(shared_ptr和weak_ptr实现的区别)

  • shared_ptr共享的智能指针

shared_ptr使用引用计数,每个shared_ptr的拷贝都指向相同的内存。在最后一个shared_ptr析构的时候,内存才会被释放。

注意事项:

1.不要用一个原始指针初始化多个shared_ptr。

2.不要再函数实参中建立shared_ptr,在调用函数以前先定义以及初始化它。

3.不要将this指针做为shared_ptr返回出来。

4.要避免循环引用。

  • unique_ptr独占的智能指针:

<1>Unique_ptr是一个独占的智能指针,他不容许其余的智能指针共享其内部的指针,不容许经过赋值将一个unique_ptr赋值给另一个 unique_ptr。

<2>unique_ptr不容许复制,但能够经过函数返回给其余的unique_ptr,还能够经过std::move来转移到其余的unique_ptr,这样它自己就再也不 拥有原来指针的全部权了。

<3>若是但愿只有一个智能指针管理资源或管理数组就用unique_ptr,若是但愿多个智能指针管理同一个资源就用shared_ptr。

  • weak_ptr弱引用的智能指针:

弱引用的智能指针weak_ptr是用来监视shared_ptr的,不会使引用计数加一,它无论理shared_ptr内部的指针,主要是为了监视shared_ptr的生命周期,更像是shared_ptr的一个助手。 weak_ptr没有重载运算符*和->,由于它不共享指针,不能操做资源,主要是为了经过shared_ptr得到资源的监测权,它的构造不会增长引用计数,它的析构不会减小引用计数,纯粹只是做为一个旁观者来监视shared_ptr中关连的资源是否存在。 weak_ptr还能够用来返回this指针和解决循环引用的问题。

  1. 智能指针的循环引用

44.  遇到coredump要怎么调试???

  • core dump又叫核心转储。当程序运行过程当中发生异常, 程序异常退出时, 由操做系统把程序当前的内存情况存储在一个core文件中, 叫core dump。

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运算

  • c++11提供的<random>实现了随机数库,它经过随机数引擎(random_number_engines)产生随机数序列,随机数分布类(random-number distribution)使用随机数引擎生成服从特定几率分布的随机数。

http://www.javashuo.com/article/p-pqeqhgcx-hm.html

  • tuple是一个固定大小的不一样类型值的集合,是泛化的std::pair

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>(); //获取第二个值

  • 引入了emplace_front、emplace、emplace_back,这些操做构造而不是拷贝元素。这些操做分别对应于push_front、insert、push_back。Emplace成员使用这些参数在容器管理的内存空间中直接构造函数。例如,在c的末尾构造一个Sales_data

   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++的四种强制转换

  • static_cast:能够实现相同类型之间的转换,如:double b ;int  a = static_cast<int>(b);
  • reinterpreter_cast :能够实现不一样类型之间的转换,如指针到整型

int* aas = new int[1] ;

aas[0] = 1;

int b = reinterpret_cast<int>(aas);   ////b中的值为aas 指针的地址

  • const_cast :中的类型必须是指针、引用或指向对象成员类型的指针

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实现的是多态类之间的的转换

对于“向上转换”(即派生类指针或引用类型转换为其基类类型),不管是指针仍是引用向上转换都是安全地。

对于“向下转型”有两种状况:

一种是基类指针所指对象是派生类类型的,这种转换是安全的;另外一种是基类指针所指对象为基类类型,在这种状况下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

  • decltype类型指示符

Const int c = 0,&b = c;

Decltype(c) x =3;//x是const int 类型

Decltype(b) y = x;//y是const int&类型;因此定义y时必须初始化

  • auto

使用auto也能在一条语句中声明多个变量,由于一条语句只能有一个基本的数据类型。

Auto定义的变量必须有初值,这样才能推导出类型是什么

57.  inline和宏定义的区别

(1)内联函数在编译时展开,宏在预编译时展开;

(2)内联函数直接嵌入到目标代码中,宏是简单的作文本替换;

(3)内联函数有类型检测、语法判断等功能,而宏没有;

(4)inline函数是函数,宏不是;

(5)宏定义时要注意书写(参数要括起来)不然容易出现歧义,内联函数不会产生歧义;

  1. C++和C的类型安全

59.  调试程序的方法:

概括法、演绎法、测试法,回溯法,暴力法。设置断点,进入调试,单步运行,进入函数,查看变量。linux gbk,win windbg

60.  类型别名

Typedef  double  wages; //wages是double的同义词

Typedef  wages   base, *p; // base是double的同义词,p是double*的同义词

相关文章
相关标签/搜索