C++面试题

综合篇

static的用处

  1. 修饰全局变量和函数(C语言):全局变量和函数只能被当前文件访问,不用担心外部命名冲突。
  2. 修饰函数内部变量:静态函数,分配在堆里,各个函数实例共享。
  3. 修饰类成员变量:只能够有静态函数访问。
  4. 修饰类成员函数:不属于某个对象,而属于整个类,使用类加上”::“访问。

左值和右值是什么?

左值:可以取地址的、有名字的,例如普通定义变量、变量的引用,指针。

右值:不可取地址的或者没有名字的,例如常量、表达式(a+b)、函数返回值func()。

右值引用(&&)是什么?

顾名思义,对右值的引用,c++11使用,可以用于给寿命短暂的右值进行续命。

注意,右值引用本身是左值,是可以取地址的。

// 语句结束后,func()临时变量会被销毁。
int a = func();
// 语句结束后,func()临时变量会被续命,生命周期延长至和b一样。
int &&b = func();
int &&c = 1;
struct A
{
A(){}
A(const A& a){}
~A(){}
};
A GetA()
{
return A();
}
int main() {
A a = GetA(); // 构造函数执行两次
A &&b = GetA(); // 构造函数执行一次
const A &b = GetA(); // 构造函数执行一次
A &d = GetA(); // 非const引用右值,非法
return 0;
}

上面的代码中,A的构造函数实际上被执行了两次,第一次在GetA()里,第二次是在A a=GetA()处。

如果使用右值引用,则只构造函数只执行一次,减少临时变量构造函数的执行次数。

lambda表达式是什么,怎么使用?

匿名函数,在需要传入函数的地方使用,非常方便,例如sort函数。

模板篇

模板函数是什么,什么时候用?

抽象数据类型,减少因为类型而重复定义的函数。

模板函数能否在cpp文件中定义,为什么?

不能,因为模板函数在用户调用时才能确定实例化成哪一种类型,而定义在cpp文件中的模板函数实际上是空的,link时是无法找到实例化函数的。 定义在头文件中时,头文件会随着用户代码一起编译,此时便能生成具体的函数。

内存碎片如何生成,怎么处理?

频繁地动态分配内存,尽量静态分配好内存。

多态篇

多态如何实现?

对象的实例内存上其实地址存放一个指针,指向虚函数表,不同类型的对象虚函数表不一样。

虚函数表有多少张?

基类数量+子类数量,假设基类A派生出B和C,那么一共有3张虚函数表。

多态模式中子类的析构函数有什么要注意的,为什么?

子类的析构函数要设置为虚函数,这样,父类指针指向子类对象并且析构时,才会先执行子类的析构函数,后执行父类的析构函数。

否则,程序只会执行父类的析构函数,造成子类的内存泄漏等问题。

STL篇

map和unordered_map如何实现?

map基于红黑树实现。

unordered_map基于hashmap实现。

hashmap一种实现结构?

数组,每一个数组存放一个值,同时数组也是链表的表头,链表存放哈希键值冲突的元素。 如果链表过长,则转换成红黑树。

其他

多线程要注意什么,要怎么做?

要注意共享变量的互斥,即不能同时又读又写,或者同时写。

为什么会出现死锁问题,如何避免?

在临界区域等待其他的锁。

避免方法:消费者不要在临界区域等待其他的锁,如果是小变量,临界区可以直接执行拷贝操作,如果是大内存,则拷贝指针。 不要把等待各个数据这种行为放到临界区域。

有那些智能指针,有何作用?

shared_ptr,普通的智能指针,

unique_ptr,只允许有一个引用者的智能指针

weak_ptr,能否防止循环引用的智能指针。

智能指针如何实现?

static类型的引用计数,指针本身,外加锁。

进程和线程的区别?

进程内的量,共享一个内存空间,切换开销大,操作系统资源分配基本单位。

线程属于进程,多个线程共享一个堆和方法区,每个线程有自己的栈,程序计数器,切换开销小,处理器调度和执行的基本单位。