笔试面试经典问题
const和#define的区别:
1. 编译器处理方式不同
define宏是在预处理阶段展开。
const常量是编译运行阶段使用。
2. 类型和安全检查不同
define宏没有类型,不做任何类型检查,仅仅是展开。
const常量有具体的类型,在编译阶段会执行类型检查。
3. 存储方式不同
define宏定义不占内存
const常量会在内存中分配(可以是堆中也可以是栈中)。
4. const常量有适当的作用域,而宏常量的作用域是全局的,也就是说宏常量不具有封装性。
5. const常量可以取地址,宏常量不可以取地址。
const作用
在c++中,const是类型修饰符。
1. 使用const修饰的变量,值不能被更改;
2. const还可以修饰函数参数、函数返回值和函数本身;
3. 函数可以根据是否为const进行重载;
类中const成员初始化:
在初始化列表中初始化。
static作用
static用来控制变量的存储方式和可见性。
1. static变量存储在程序的静态存储区。(函数执行结束不会释放存储空间)
2. static把变量的可见范围限制在编译单元中,使它成为一个内部链接。
总结:
static总是使得变量或对象的存储形式变成静态存储,链接方式变成内部链接。对于局部变量(已经是内部链接了),它仅改变其存储方式;对于全局变量(已经是静态存储了),它仅改变其链接类型。
类中的static成员:
static成员和类相关,而不是和类的对象相关。
项目中有没有用过static?
全局变量、静态全局变量、静态局部变量:
当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量只在定义该变量的源文件内有效,在同一源程序的其他源文件中不能使用它。 (把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。)
static成员初始化:
static成员必须在类定义体的外部定义,而且应该在定义时初始化。但是,整形const static数据成员就可以在类的定义体中进行初始化。
函数局部static变量初始化:
c++中,在第一次用到时才会被初始化(lazy initialization)。
全局static变量的初始化在编译的时候进行。
extern关键字:
C和C++对函数的处理方式(名称修饰)是不同的.extern "C"是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明。
volatile关键字:
volatile的本意是“易变的”,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。关键字volatile是给编译器的指示,指出对这样的对象不应该执行优化。
volatile基本可以做到两件事情:
1. 阻止编译器为了提高速度将一个变量缓存到寄存器内而不写回。
2. 阻止编译器调整操作volatile变量的指令顺序。
模板和宏的区别:
1. 模板是类型安全的,宏不是。
2. 宏在预编译阶段直接展开,而模板只有在用到时才实例化。
3. 模板的功能更加强大,支持偏特化、支持模板元编程。
指针和引用的区别:
1. 指针是一个实体,在逻辑上是独立的;引用是个别名,在逻辑上不是独立的
2. 指针可以为空,引用不可以为空
3. 引用需要初始化,指针不需要
4. 指针可以被重新赋值,而引用总是指向最初的对象
(引用在语言内部是用指针实现的,引用就是一个常量指针,当然也占内存)
C 中指针和引用的物理实现是一回事,都是内存地址;两者的区别是在编译时编译器无法对指针操作进行类型检查,而对引用可以。这也是引用更安全的原因。
引用是类型安全的,而指针不是
引用传递与指针传递的区别?
指针传递参数的本质是值传递,引用的任何操作都处理成间接寻址
堆和栈的区别:
1. 空间分配
栈:由操作系统自动分配释放
堆:一般由程序员分配释放
2. 栈的存取速度快
3. 栈是一块连续的内存区域。堆是不连续的内存区域。
4. 堆获得的空间比较灵活,也比较大。
【栈中存放的内容:
1. 函数返回地址、函数参数
2. 函数局部变量
3. 上下文:比如函数调用前后需要保证不变的寄存器值】
memcpy和memmove函数的区别?
void * memcpy (void * dest, const void *src, size_t n);
void * memmove(void *dest, const void *src, size_t n);
作用都是从源内存拷贝n个字节到目标内存。不同的是,当源内存和目标内存发生重叠时,memcpy会出现错误,而memmove能正确地实施拷贝,但这也增加了一点点开销。
new/delete和malloc/free的区别:
1. malloc是标准库函数,new是c++运算符,它们都可用于申请动态内存。
2. 对于非内部数据类型的对象而言,对象在创建的同时要自动执行构造函数。malloc无法满足要求,而new可以完成动态内存分配和初始化工作。
3. new自动计算需要分配的空间,而malloc需要手工计算。
4. new是类型安全的,而malloc不是。
5. malloc需要头文件支持,new不需要。
6. new在实现上其实调用了malloc函数。
重载(overload)、覆盖(override) = 重写、隐藏(hide)的区别:
重载:
1. 相同作用域(在同一个类中);
2. 名字相同;
3. 形参表不同;
覆盖:(派生类函数重写基类函数)
1. 不同作用域(分别位于基类与派生类);
2. 名字相同;
3. 形参表相同、返回值相同;
4. 基类函数必须有virtual关键字;
隐藏:(派生类函数屏蔽基类函数)
1. 派生类函数与基类函数名字相同、形参表不同,不论有无virtual关键字,基类函数将被隐藏。
2. 派生类函数与基类函数名字相同、形参表相同,但是基类函数无virtual关键字,基类函数将被隐藏。
深拷贝(deep copy)与浅拷贝(shallow copy)的区别:
如果一个对象拥有资源,复制对象时连同对象拥有的资源一起复制,就是深拷贝,没有复制对象拥有的资源就是浅拷贝。
浅拷贝也称位拷贝。
默认的拷贝构造函数和赋值操作符进行的都是浅拷贝。
浅拷贝缺点:
多个对象指向一块内存,当某个对象释放内存后,其他指向这块内存的对象就会出错。
当数据成员中有指针时,必须使用深拷贝。
string三种实现方式:
1. 直接拷贝(eager copy)
2. Copy-on-Write(COW,写时复制)
Copy-On-Write一定使用了“引用计数”
基于”共享“和”引用“计数的COW在多线程环境下必然面临线程安全的问题。
"The COW is dead, long live eager-copy"
3. 短字符串优化(SSO, Short String Optimization)
http://www.cnblogs.com/promise6522/archive/2012/03/22/2412686.html
数组、链表、hash的区别:
1. 数组中的元素在内存中是连续存储的,链表中的元素在内存中不是连续存储的。
数组支持随机存取,查找效率高,但插入、删除效率低。链表查找效率低,但插入、删除效率高。
2. 数组必须事先定义固定的长度,不能适应数据动态增减的情况。
有什么方式既能够具备数组的快速查询的优点又能融合链表方便快捷的增加删除元素的优势?HASH呼之欲出。
hash表处理冲突:
1. 开放地址法:
线性探测再散列、二次探测再散列、伪随机探测再散列
2. 再哈希法
3. 链地址法
一致性hash:
一致性hash算法的主要目的是为了尽量减少数据迁移
每天进步一点点——五分钟理解一致性哈希算法(consistent hashing)
查找:
顺序查找、二分查找、二叉排序树查找、哈希表查找、分块查找
——————————————————————————————————————