2017模拟面试题库 —— C++相关
来源:互联网 发布:epud阅读器 mac 编辑:程序博客网 时间:2024/06/08 15:03
Q:指针和引用的区别?
A:在x86 32位 Linux系统下,指针占4个字节;
从底层实现上来看:
1. 引用也是一个指针,创建一个指针和创建一个引用的汇编指令是一样的
int a = 8;011A5F6E mov dword ptr [a],8 int * p = &a;011A5F75 lea eax,[a] # 将变量a地址装入寄存器eax011A5F78 mov dword ptr [p],eax # 将寄存器eax中的地址传入指针pint & q = a;011A5F7B lea eax,[a] # 将变量a地址装入寄存器eax011A5F7E mov dword ptr [q],eax # 将寄存器eax中的地址传入指针q
2. 以引用 / 指针改变内存的汇编指令相同
*p = 10;00F25F81 mov eax,dword ptr [p] # 将指针p中的地址传入寄存器eax00F25F84 mov dword ptr [eax],0Ah # 将值传入eax中地址所指向内存q = 10;00F25F8A mov eax,dword ptr [q] # 将指针q中的地址传入寄存器eax00F25F8D mov dword ptr [eax],0Ah # 将值传入eax中地址所指向内存
从表现形式上来看:
1. 引用自带解引用
2. 没有空引用,引用必须在定义的时候初始化,且引用一经初始化不能改变 *相当于 int * const p
3. 指针有多级指针 / 引用只有一级引用 *C11支持二级引用(左值、右值引用)
/*************************************************************************************/
Q:C++的函数重载?
A:C/C++ 是编译型语言,源文件经过编译生成目标文件,收集源文件中的函数/变量生成符号表,接着在链接中进行符号解析;
符号表中的函数不允许重复定义,在C中是以 函数名 为区分函数的方式,在C++中则是以 函数名 + 参数列表 区分;(所以C中不能函数重载)
* 函数重载的前提:在同一个作用域下* 特例:对于const修饰的变量,在编译器眼里是相同的,不构成函数重载
(只有用 const 修饰指针 / 引用 才能构成重载)
/*************************************************************************************/
Q:volitale关键字是做什么用的?
A:
1. 防止编译器对指令顺序进行调整
/* 防止Cpu调整指令顺序:barrier( ) */
2. 防止多线程对共享变量进行缓存(保证所有线程都能实时获得共享变量的值)
/* 只能保证可见性,不能保证原子性(mutex、信号量、读写锁、原子锁) */
/*************************************************************************************/
Q:内存泄漏 / 资源泄漏
A:
1.调用 malloc/new 未 free/delete
malloc/new 后忘记 free/delete 或在执行 free/delete 之前抛出异常
2.发生浅拷贝对象默认赋值
eg:
Class Test{private: int * p;public: Test() { p = (int*)malloc(sizeof(int)); } ~Test() { free(p); }};int main(){ Test A; Test B(A); // 此处发生浅拷贝,当调用默认拷贝构造函数时,A、B中的指针指向同一块内存 // 当A、B析构时,同一块内存被free两次,程序将崩溃 Test C; C = A; // 此处发生浅拷贝,当调用默认赋值函数时,A、C中的指针指向同一块内存 // C中指针原指向的内存将丢失,发生内存泄漏 return 0;}
3.基类指针指向堆区资源,而基类析构函数非虚,则派生类无法析构
基类指针实际指向的是派生类对象中基类对象的起始地址,若基类中无虚函数,则派生类自己生成的虚表指针在内存布局中会在基类对象上方,导致 delete 时偏移量错误
class Base{private:int * p;public:Base() {p = (int *)malloc(sizeof(int));}~Base() // 此处应为虚函数{cout<<"hello"<<endl;free(p);}};class Test : public Base{public:Test() {}~Test() { cout<<"world"<<endl; }};int main(){Base * pB = new Test(); // 以基类指针指向派生类对象delete pB; // 只调用了Base的析构函数(根据指针类型静态绑定)return 0;}
将基类 Base 的析构函数定义为虚函数后,形成动态绑定;
调用析构函数时查虚表,发现派生类 Test 重写了析构函数,则调用 Test 的析构函数 -> 调用父类 Base 析构函数,对象被成功的析构掉。
4.socket / fd 未 close (fd 进程上限: 2^16 = 65535 , 即 epoll 可操作 fd 上限)
5. 产生僵尸进程,进程内核栈内存泄漏(8k 底端PCB)
6.new Test[100] -> delete Test
使用 new 一次生成多个对象,而没有使用 delete [] ,多个对象只会调用一次析构函数
7. 构造函数中抛出异常(new -> bad_alloc),退出时未调用析构函数,申请的内存将无法释放
8. 智能指针的交叉引用
class B;class A{public:shared_ptr<B> _pb;};class B{public:shared_ptr<A> _pa;};int main(){shared_ptr<A> pa(new A); // A引用计数加一(1)shared_ptr<B> pb(new B); // B引用计数加一(1)pa->_pb = pb; // B引用计数加一(2)pb->_pa = pa; // A引用计数加一(2)return 0;}当函数退出时,调用 B 的析构函数,B 的引用计数 - 1 = 1,B 没有析构掉;
接着调用 A 的析构函数,A 的引用计数 - 1 = 1,A 也没有析构掉,A、B 的内存泄漏。
/*************************************************************************************/
Q:C++多态的原理?
A:C++中的多态分为静态多态和动态多态,也称为编译期多态和运行期多态:
静态多态包括:重载、模板
动态多态包括:虚函数
C++通过虚函数实现动态多态:
在基类的函数前加上 virtual 关键字,在派生类中重写该函数,以指针或引用调用类从成员函数会发生多态,运行时根据对象的实际类型调用相应的函数;
原理:
对于每个定义了虚函数的类和它的派生类,都会产生一个虚函数表,这个虚函数表是类共享的;
每个对象前四个字节都有一个虚表指针 vf_ptr,指向该类型的虚函数表vf_table(虚表存放在只读数据段 .rodata )
动态联编在 基类指针 指向不同的 派生类对象 时发生,若派生类对象重写了虚函数,则虚表对应项将被覆盖;
实际调用中根据对象的 vf_ptr 找到该类的虚表,调用对应的函数
拥有虚函数的类的构造函数过程:
push ebp # 将栈底保存mov ebp, esp # 将栈顶指针值赋给栈底指针sub esp, 4ch # 开辟栈帧ebp <-> esp 0xCCCCCCCC # 刷新栈帧vftable -> vfptr # 将虚表地址赋给虚表指针
/*************************************************************************************/
Q:早绑定/晚绑定 、 静态绑定/动态绑定?
A:早绑定又称静态绑定,在程序编译期发生,即编译期就确定将要调用的函数地址(以对象直接调用成员函数);
晚绑定又称动态绑定,在程序运行期发生,即在程序运行中确定要调用的函数地址(前提:以指针或引用调用成员函数,且类中该函数定义为虚函数,调用不在构造函数中——即该对象存在,可取地址);
p->Show(); # 静态绑定 0139B5D8 push 0Ah # 参数压栈0139B5DA mov ecx,dword ptr [p] 0139B5DD call Base::Show (013916BDh) # 调用函数
p->Show(); # 动态绑定009AA858 mov esi,esp009AA85A push 0Ah # 参数压栈009AA85C mov eax,dword ptr [p] # 将对象前四个字节(vfptr)放入eax009AA85F mov edx,dword ptr [eax] # 将 vftable 地址放入edx009AA861 mov ecx,dword ptr [p] 009AA864 mov eax,dword ptr [edx] # 查虚表得到虚函数地址009AA866 call eax # 调用相应的虚函数009AA868 cmp esi,esp 009AA86A call __RTC_CheckEsp (09A147Eh) # 调整栈平衡
/*************************************************************************************/
Q:智能指针
A:一种确定性的通用垃圾收集机制。
智能指针和普通指针的区别在于智能指针加了一层封装,目的是为了方便的管理一个对象的生命周期;
实质是一个对象,行为表现却像一个指针;
* 智能指针更深层的意义在于,值语义到引用语义的转换;
C++工程实践经验谈——陈硕:值语义与数据抽象
http://www.cnblogs.com/Solstice/archive/2011/08/16/2141515.html
// TODO
shared_ptr 强智能指针
直接持有资源,可直接修改资源,其中的计数器记录资源有多少个强智能指针引用
weak_ptr 弱智能指针
不共享资源,只有资源的观测权,通过观测引用资源的强智能指针计数来管理资源,它的构造不引起资源引用计数的增加
(创建对象时用强智能指针,其他地方使用只能持有资源的弱智能指针;避免交叉引用导致资源无法释放)
需要特别指出的是,如果shared_ptr所表征的引用关系中出现一个环,那么环上所述对象的引用次数都肯定不可能减为0那么也就不会被删除,为了解决这个问题引入了weak_ptr。
------
例:多线程访问共享对象的线程安全
1. 主线程创建对象 Test
2. 子线程调用 Test 的成员方法(Test 可能已经被析构)
解决方法:
1. 以强智能指针创建对象
shared_ptr<Test> pa(new Test());
2. 将弱智能指针传入子线程
weak_ptr<Test> pb = *(weak_ptr<Test> *)lparg;
通过观察强智能指针的引用计数,来判断该对象是否可用。
void * pthread(void * arg){ /* ... ... */ shared_ptr<Test> pc = pb.lock(); // 将弱智能指针提升成强智能指针 if(pc != NULL) // 判断对象是否存在 { pc->func(); } /* ... ... */}
智能指针实现:https://github.com/chen892704/STL-Learning
/*************************************************************************************/
C++多继承与虚继承的内存布局:
https://www.oschina.net/translate/cpp-virtual-inheritance?p=1#comments
继承与多态——要点总结
继承结构中构造函数的调用:
push ebp # 保存栈底
mov ebp, esp # 将栈顶设为新的栈底
sub esp, 4ch # 开辟新的栈帧
rep stos ... # 将新的栈帧刷为0xCCCCCCCCh
vftable -> vfptr # 将当前类的虚表地址赋给创建对象的虚表指针
(每一层构造函数都会将当前类的虚函数表地址写入虚表指针中)
虚函数表内容:包括虚函数指针,以及运行时的信息;
用基类指针指向堆区派生类对象时,指针指向的位置是派生类对象中基类部分的起始位置;
若派生类实现了虚函数而基类没有,则对象中的虚表指针将在派生类对象的前面,导致调用 delete 释放内存的时候,找不到派生类对象的实际起始位置(ptr - 4byte),程序崩溃;
解决的方法是在基类中定义虚函数,则派生类对象中的虚表指针将从基类继承而来,当用基类指针指向派生类对象时,基类指针将指向对象的首部。
成员能否访问 / 访问权限是否正确 / 函数的默认值用哪一个 -> 在编译期确定
包含虚函数的继承结构中,调用哪个类的虚函数 -> 在运行时确定
C++类型强转:
const_cast 去掉对象const属性的类型转换
static_cast 编译器认为较安全的类型转换
reinterpret_cast 类似于C的强转,较底层的类型转换
dynamic_cast 支持RTTI(run-time type identity)的类型转换
/*
Test * ptr = dynamic_cast<Test *>(p);
如果指针p指向的对象类型为Test,则返回Test *类型的指针,否则返回NULL
*/
- 2017模拟面试题库 —— Linux系统相关
- 2017模拟面试题库 —— C++相关
- C语言面试专用题库
- 华为工程师面试题库—通信类
- C语言面试题库1-1
- C语言面试题库1-2
- C语言面试题库1-3
- C语言面试题库1-4
- C语言面试题库1-5
- 【转】C语言面试专用题库
- 面试题库
- .NET面试相关—1
- CSDN日报20170320——《Java 程序员的面试经历和题库》
- EMC的面试题库
- JAVA面试题库
- 面试笔试题库
- 面试笔试题库
- IT 面试题库
- String类
- Qt:QML与C++混合编程详解
- startService()和bindService()区别
- chaojitaijie
- 给全景添加动态热点
- 2017模拟面试题库 —— C++相关
- 笨方法学python(本文为阅读时从此书摘录的笔记) 第三天
- 如何解决Tomcat服务器打开不了HOST Manager的问题
- 谁获得了最高奖学金
- 20170715 heforher
- 一物一码生码软件5.0发布
- HDU 1856 More is better
- 微信小程序不同页面之间的传值
- Maven打可运行jar包