对于这些问题的回答,可以按照思维导图的结构来组织答案,然后再进行回答。
C++11用过哪些特性?
(首先,要回答好这个问题,需要注意问题的层次,不要一上来就说新增了某某关键字和语法,在这里我们可以分为五个层次来跟面试官讲解:语法,std容器,多线程,智能指针,右值引用)
(这里可以对照着思维导图跟面试官说,聊到某个方面的细节还需要自行搞清楚)
动态库和静态库的区别?
首先来看动态库和静态库的演示:
先创建一个头文件
里面定义了方法,但是还未实现。
实现两个方法的实现分别在add.cc和del.cc中。
然后在创建一个main程序,来调用这两个函数
接着,再将add.cc和del.cc分别生成.o文件(机器代码)
用g++的-c选项
g++ -c add.cc -o add.og++ -c del.cc -o del.o
无论是动态库还是静态库,第一步都是需要先生成这样的目标文件。
先来看看静态库的生成:
使用命令:
ar rcs libapi.a del.o add.o
关于这个命令的了解:
这里就生成了一个libapi.a的静态库
然后再是动态库的生成:
我们只要用g++把目标文件编译进去就行了
这里顺便说下,在linux下,静态库的后缀是.a,动态库的后缀是.so。而在Windows下,静态库的后缀是.lib,动态库的后缀是.dll。
使用命令
g++ -shared -fPIC -o libapi.so del.o add.o
如果出现报错:
那么我们可以再重新把add.cc和del.cc重新编译一下:
g++ -fPIC -c del.cc -o del.og++ -fPIC -c add.cc -o add.o
然后再执行:
g++ -shared -o libapi.so del.o add.o
其中
这下两个库就都有了
静态库的使用:
g++ -static main.cc -o static_main -L./ -lapi -I./
编译是默认链接动态库的,如果要链接静态库,那么需要使用-static选项, -L表示我们要在哪个目录下搜索这个库,后面再接这个库的名字,-I(大写的i)表示头文件在哪里搜索。
执行效果:
再来使用一下动态库:
g++ main.cc -o dynamic_main -L./ -lapi -I./
主要就是去掉-static选项即可。
看一下两种链接方式生成的程序的大小:
可以看到,链接静态库生成的程序大小明显要大很多。
运行dynamic_main:
发现报错了,原因大致是找不到这个库。
原因是当我们在链接动态库的时候,编译器仅仅只是对我们调用的函数进行了语法检查,还并没有调用相对应的库。在linux下,需要我们指定一个搜索路径,这是一个宏定义。
linux默认只会在/lib 和/usr/lib这两个目录中搜索,我们的动态库并没有安装在这两个目录中。
这里我们可以用export来设置,把这个库所在的目录设置进去:
export LD_LIBRARY_PATH=/home/chika/lib-test
这个设置在会话重启后就失效了。
此时就可以运行dynamic_main了:
这就是生成动态库和静态库,以及使用的过程了。
思维导图总结:
另外静态库的可移植性也强一些,链接动态库的程序在移植后需要确保能够找到动态库。
说下面向对象的三大特征?
在继承的继承权限那里,对于基类的private权限成员,无论采用何种继承方式,在子类中都是不可见的。
在回答这个问题的时候,我们就以这个框架来回答,简略说明每个特性的目的和特性,如果还要聊到细节的部分那就再细说。
左值引用和右值引用有什么区别?右值引用有什么意义?
这里可以把左值和右值的区别,以及能解决什么问题给回答上来,不过有时候面试时会再深入到什么是左值,什么是右值,此时就需要再对左值和右值进行一个详细的介绍了。
什么是左值?什么是右值?
简述多态的原理
怎么解决菱形问题?
关键字 override 和 final的作用?
C++类型推导的用法
使用示例:
如果用auto同时定义多个变量,那么这些变量必须同一个类型:
exp是左值时,推导出左值引用:
decltype用来推导表达式的类型,不会进行运算
function lambda bind之间的关系
代码验证:
#include <iostream>
#include <functional>using namespace std;void hello(int count)
{cout << "hello count : " << count << endl;
}class Hello
{
public:static void hello(int x){cout << "static hello x : " << x << endl;}
};class CHello
{
public:void hello(int x){cout << "hello x : " << x << endl;}
};class AHello
{
public:void operator()(int x){i += x;cout << "op x : " << x << " i + x = " << i << endl;}
private:int i = 0;
};// 模式实现的[i](int x) mutable {
// ++i;
// cout << "i : " << i << "x : " << x << endl;
// };
class LambdaHello
{
public:LambdaHello(int i) : _i(i) {} // 如果是引用捕获,那么这里就是引用,如果没有mutable,那么就有const属性void operator()(int x){++_i;cout << "_i : " << _i << "x : " << x << endl;}
private:int _i;
};// auto hello9 = bind(&hello,9); 生成的仿函数对象大致如下:
class BindHello
{
public:BindHello(function<void(int)> fn,int x) : _fn(fn),_x(x){}void operator()(){_fn(_x);}
private:int _x;function<void(int)> _fn;
};int main()
{function<void(int)> hello1 = hello;hello1(1);// 也是可以是函数指针function<void(int)> hello2 = &hello;hello2(2);//也可以是类的静态函数(或者也可以是指针)function<void(int)> hello3 = Hello::hello;hello3(3);function<void(int)> hello4 = &Hello::hello;hello4(4);// 类的非静态函数CHello c;function<void(CHello*,int)> hello5 = &CHello::hello; // 此时这里就必须要取地址了hello5(&c,5);// 仿函数function<void(int)> hello6 = AHello(); // 这里调用构造函数,生成仿函数对象hello6(6); hello6(6); // 发现的i的值会变化,这种状态会变化的函数称为闭包// lambda表达式int i = 0;auto hello7 = [i](int x) mutable {++i;cout << "i : " << i << "x : " << x << endl;};hello7(2);hello7(2);cout << "i : " << i << endl; // 如果是值捕获,那么外面的i的值并没有发生改变// 模拟lambda的仿函数auto hello8 = LambdaHello(i);hello8(8);hello8(8);cout << "i : " << i << endl; // 发现是一样的效果auto hello9 = bind(&hello,9); // bind可以绑定参数来生成函数对象hello9();auto hello10 = bind(&CHello::hello,&c,10);auto hello11 = bind(&CHello::hello,&c,placeholders::_1); //可以固定参数hello10();hello11(11);auto hello12 = BindHello(&hello,12); // 模仿hello9 bind所生成的函数对象hello12();function<void(int)> hello13 = bind(&hello,placeholders::_1); // 与hello9类似hello13(13);return 0;
}
继承下,构造函数和析构函数的执行顺序
虚函数表和虚函数表指针的创建时机
虚析构函数的作用
智能指针的种类以及使用场景