c++面经(七)

欢迎关注 0voice · GitHub
3 、构造函数析构函数可否抛出异常
C++ 只会析构已经完成的对象,对象只有在其构造函数执⾏完毕才算是完全构造妥当。在构造函数中发⽣异常,控
制权转出构造函数之外。因此,在对象 b 的构造函数中发⽣异常,对象 b 的析构函数不会被调⽤。因此会造成内存
泄漏。
auto_ptr 对象来取代指针类成员,便对构造函数做了强化,免除了抛出异常时发⽣资源泄漏的危机,不再需要
在析构函数中⼿动释放资源;
如果控制权基于异常的因素离开析构函数,⽽此时正有另⼀个异常处于作⽤状态, C++ 会调⽤ terminate 函数让程
序结束;
如果异常从析构函数抛出,⽽且没有在当地进⾏捕捉,那个析构函数便是执⾏不全的。如果析构函数执⾏不全,就
是没有完成他应该执⾏的每⼀件事情。
4 、类如何实现只能静态分配和只能动态分配
前者是把 new delete 运算符᯿载为 private 属性。
后者是把构造、析构函数设为 protected 属性,再⽤⼦类来动态创建
建⽴类的对象有两种⽅式:
静态建⽴,静态建⽴⼀个类对象,就是由编译器为对象在栈空间中分配内存;
动态建⽴, A *p = new A(); 动态建⽴⼀个类对象,就是使⽤ new 运算符为对象在堆空间中分配内存。这个过
程分为两步,第⼀步执⾏ operator new() 函数,在堆中搜索⼀块内存并进⾏分配;第⼆步调⽤类构造函数构
造对象;
只有使⽤ new 运算符,对象才会被建⽴在堆上,因此只要限制 new 运算符就可以实现类对象只能建⽴在栈
上。可以将 new 运算符设为私有。
1 C++ STL 介绍(内存管理, allocator ,函数,实现机理,多线程实
现等)
STL ⼀共提供六⼤组件,包括容器,算法,迭代器,仿函数,配接器和配置器,彼此可以组合套⽤。容器通过配置
器取得数据存储空间,算法通过迭代器存取容器内容,仿函数可以协助算法完成不同的策略变化,配接器可以应⽤
于容器、 仿函数和迭代器。
容器: 各种数据结构,如 vector list deque set map ,⽤来存放数据, 从实现的⻆度来讲是⼀种类模板。
算法: 各种常⽤的算法,如 sort (插⼊,快排,堆排序), search (⼆分查找), 从实现的⻆度来讲是⼀种⽅法
模板。
迭代器: 从实现的⻆度来看,迭代器是⼀种将 operator*,operator->,operator++, operator-- 等指针相关操作赋予
᯿载的类模板,所有的 STL 容器都有⾃⼰的迭代器。
仿函数: 从实现的⻆度看,仿函数是⼀种᯿载了 operator() 的类或者类模板。 可以帮助算法实现不同的策略。
配接器: ⼀种⽤来修饰容器或者仿函数或迭代器接⼝的东⻄。
配置器: 负责空间配置与管理,从实现的⻆度讲,配置器是⼀个实现了动态空间配置、空间管理,空间释放的类模
板。
扩展: 内存管理 allocator
SGI 设计了 双层级配置器 ,第⼀级配置器直接使⽤ malloc() free() 完成内存的分配和回收。第⼆级配置器则根据
需求ᰁ的⼤⼩选择不同的策略执⾏。 对于 第⼆级配置器 ,如果需求块⼤⼩⼤于 128bytes ,则直接转⽽调⽤第⼀级配置器,使⽤ malloc() 分配内存。如
果需求块⼤⼩⼩于 128bytes ,第⼆级配置器中维护了 16 个⾃由链表,负责 16 种⼩型区块的次配置能⼒。
即当有⼩于 128bytes 的需求块要求时,⾸先查看所需需求块⼤⼩所对应的链表中是否有空闲空间,如果有则直接
返回,如果没有,则向内存池中申请所需需求块⼤⼩的内存空间,如果申请成功,则将其加⼊到⾃由链表中。如果
内存池中没有空间,则使⽤ malloc() 从堆中进⾏申请,且申请到的⼤⼩是需求ᰁ的⼆倍(或⼆倍+ n 附加ᰁ),⼀
倍放在⾃由空间中,⼀倍(或⼀倍+ n )放⼊内存池中。
如果 malloc() 也失败,则会遍历⾃由空间链表,四处寻找 尚有未⽤区块,且区块够⼤ freelist ,找到⼀块就挖出
⼀块交出。如果还是没有,仍交由 malloc() 处理,因为 malloc() out-of-memory 处理机制或许有机会释放其他
的内存拿来⽤,如果可以就成功,如果不⾏就报 bad_alloc 异常
STL 中序列式容器的实现
vector
是动态空间,随着元素的加⼊,它的内部机制会⾃⾏扩充空间以容纳新元素。 vector 维护的是⼀个连续的线性空
间,⽽且普通指针就可以满⾜要求作为 vector 的迭代器( RandomAccessIterator )。
vector 的数据结构中其实就是三个迭 代器构成的,⼀个指向⽬前使⽤空间头的 iterator ,⼀个指向⽬前使⽤空间尾
iterator ,⼀个指向⽬前可⽤空间尾的 iterator 。当有新的元素插⼊时,如果⽬前容ᰁ够⽤则直接插⼊,如果容ᰁ
不够,则容ᰁ扩充⾄两倍,如果两倍容ᰁ不⾜, 就扩张⾄⾜够⼤的容ᰁ。
扩充的过程并不是直接在原有空间后⾯追加容ᰁ,⽽是᯿新申请⼀块连续空间,将原有的数据拷⻉到新空间中,再
释放原有空间,完成⼀次扩充。需要注意的是,每次扩充是᯿新开辟的空间,所以扩充后,原有的迭代器将会失
效。
list
vector 相⽐, list 的好处就是每次插⼊或删除⼀个元素,就配置或释放⼀个空间,⽽且原有的迭代器也不会失
效。 STL list 是⼀个双向链表,普通指针已经不能满⾜ list 迭代器的需求,因为 list 的存储空间是不连续的。 list
迭代器必需具备前移和后退功能,所以 list 提供的是 BidirectionalIterator list 的数据结构中只要⼀个指向 node
节点的指针就可以了。
deque
vector 是单向开⼝的连续线性空间, deque 则是⼀种双向开⼝的连续线性空间。所谓双向开⼝,就是说 deque
持从头尾两端进⾏元素的插⼊和删除操作。相⽐于 vector 的扩充空间的⽅式, deque 实际上更加贴切的实现了动
态空间的概念。 deque 没有容ᰁ的概念,因为它是动态地以分段连续空间组合⽽成,随时可以增加⼀段新的空间并
连接起来。
由于要维护这种整体连续的假象,并提供随机存取的接⼝(即也提供 RandomAccessIterator ),避开了 ᯿新配
置,复制,释放 的轮回,代价是复杂的迭代器结构。也就是说除⾮必要,我们应该尽可能 的使⽤ vector ,⽽不是
deque
那么我们回过来具体说 deque 是如何做到维护整体连续的假象的, deque 采⽤⼀块所谓的 map 作为主控,这⾥
map 实际上就是⼀块⼤⼩连续的空间,其中每⼀个元素,我们称之为节点 node ,都指向了另⼀段连续线性空间
称为缓冲区,缓冲区才是 deque 的真正存储空间主体。
SGI STL 是允许我们指定 缓冲区的⼤⼩的,默认 0 表示使⽤ 512bytes 缓冲区。当 map 满载时,我们选⽤ ⼀块更
⼤的空间来作为 map ,᯿新调整配置。 deque 另外⼀个关键的就是它的 iterator 的设计, deque iterator 中有
四个部分, cur 指向缓冲区现⾏元素, first 指向缓冲区的头, last 指向缓冲区的尾(有时会包含备⽤空间), node
指向管控中⼼。所以总结来说, deque 的数据结构中包含了,指向第⼀个节点的 iterator start , 和指向最后⼀个节
点的 iterator finish ,⼀块连续空间作为主控 map ,也需要记住 map 的⼤⼩,以备判断何时配置更⼤的 map stack
是⼀种先进后出的数据结构,只有⼀个出⼝, stack 允许从最顶端新增元素,移除最顶端元素,取得最顶端元素。
deque 是双向开⼝的数据结构,所以使⽤ deque 作为底部结构并封闭其头端开⼝,就形成了⼀个 stack
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值