当前位置: 首页 > 工具软件 > Boost > 使用案例 >

Boost学习笔记(一)——Boost使用基础、内存管理

诸葛苏燕
2023-12-01

一、Boost使用基础

Boost库的大部分组件(90%左右),不需要进行编译,直接包含头文件即可使用。
#include <boost/logic/tribool.hpp>
using namespace std;

Boost网站

www.boost.org

C++标准库的一个实现——STLport

配合boost库工作
可移植性高,几乎可以配合市面上所有的操作系统合编译器来使用

如果想要深入了解,推荐大家看一下《Boost完全开发手册这本书》,虽然是十年前的老书,但有诸多可以复用的经验,读后使人受益匪浅。

二、内存管理

由于指针的灵活性与复杂性,C++在使用指针不当时,会导致内存泄漏、野指针、访问越界等一系列的问题。
C++提供了auto_ptr智能指针,但仍有不足。
boost库中提供了想java垃圾回收机制一样的一套高效的管理内存方式。

1. Smart_prt库

1.1 RALL机制

在使用资源的类的构造函数中申请资源,在相应的析构函数中释放资源。
在声明栈上局部变量时申请的资源,可以在离开作用域后自动释放,而使用new申请的资源,需要程序员显式的进行相应的delete操作才能释放,这就造成了内存泄漏的安全隐患。

1.2 智能指针

在退出作用域时,不管是正常退出还是异常退出,都自动调用delete释放在堆中动态申请的资源。
智能指针有很多种,例如:

std::auto_prt
  1. return 之前调用delete
  2. catch异常之后,调用delete
int main()
{
	auto_ptr < class_need_resource > p1 (new class_need_resource);
	auto_ptr <demo_class> p2(demo_class.creat());
} 	// 离开作用域后,p1,p2自动析构,释放占用的内存空间

遗憾的是,C++标准库并没有覆盖智能指针的全部领域,特别是引用计数型智能指针。

1.3 boost对智能指针的实现

boost.smart_ptr库是对C++98标准的一个绝对补充,包含了六种智能指针:

  1. scoped_ptr
  2. scoped_array
  3. shared_ptr
  4. shared_array
  5. weak_ptr
  6. instrusive_ptr

以上六种智能指针,都是轻量级的组件,速度与原始指针相差无几,对于所指向的类型T也仅有一个很小且很合理的要求:类型T不能抛出异常。

如何使用:

include <boost/smart_prt>
using namespace boost;

2. scoped_ptr

2.1 基本介绍

类似auto_ptr的智能指针,保证动态创建的对象在任何时候都能被正确的删除。

scoped_ptr的所有权限制更加严格,不能转让,一旦scoped_ptr获得了对象的所有权,你就不能从它那里收回来。

scoped_ptr同时把拷贝构造函数和赋值操作符都声明为私有,禁止对指针的赋值操作,保证了它管理的指针不能被转让所有权。

成员函数reset的功能是重置scope的_ptr,这违背了设计的初衷。
非必须的情况下,尽少使用这种用法。

scoped_ptr重载了运算符*和运算符->,来模仿被代理的原始指针的行为,便于将Scoped_ptr对象如同原始指针对象一样来使用。如果,scoped_ptr保存了空指针,则此行为未定义。

scoped_ptr指针不能进行比较操作,因为==和!=运算符都已经被声明为私有,但是提供了一种在bool语境中自动转化为bool值的用法,通过这一用法可以判断指针是否为空。

成员函数swap用来交换两个指针的值,操作高效,被用于实现reset函数。

成员函数get用来返回此局部指针所对应的原始指针,一般用于如底层是C语音必须需要原始指针的场景等。
注意,千万不要对返回的指针进行delete操作。
否则,程序会面临崩溃的危险。

2.2 与auto_ptr的对比

scoped_ptr和auto_ptr的用法基本一致,大多数情况下可以相互替换,它可以从auto_ptr获得指针的管理权——同时auto_ptr失去指针的管理权。

与auto_ptr一样,两者都不能用作容器的元素,不过原因不同。
auto_ptr是因为它的转移语义,而scoped_ptr是因为他不能拷贝和赋值,不符合容器对元素的要求。

auto_ptr的指针所有权是可以转移的,可以在函数间传递,同一时刻只能有一个auto_ptr来进行管理。

而scoped_ptr因为私有化了赋值和拷贝操作,指针所有权不能转移。

3.scoped_array

scoped_array弥补了标准库中没有指向数组的智能指针的遗憾。

基本介绍

构造函数接受的指针必须是new【】的结果
重载了【】运算符,可以像普通数组一样使用下标访问元素,但越界时会发生意想不到的错误。
但不能使用*和->运算符,因为没有定义。

只能在声明的作用域内使用,不能拷贝和赋值。

由于并未定义很多操作,使用scoped_array与使用正常数组速度一样快,但是不能作为函数和容器等的接口,也不能动态增长。

因此,如果要使用动态数组的话,尽量使用std::vector,它只引入了很小的开销,提供了更高的安全性和灵活性。

4. shared_ptr

4.1 与scoped_ptr的比较

shared_ptr是最像指针的智能指针,是boost.smart_ptr库中,最有价值、最重要的组成部分。

引用计数型的智能指针,可以被自由的拷贝和赋值,在任意的地方共享它,当没有代码使用它(引用计数为0时),才自动删除被包装的对象。

shared_ptr可以安全的放到标准的容器中,作为容器的元素。

shared_ptr与scoped_ptr一样都是管理new动态分配对象的智能指针,因此功能上有许多的相似之处。

  1. 他们都重载了*和->操作符,以模仿原始指针的行为
  2. 都隐式的提供bool类型转换,以判断指针的有效性
  3. get可以得到原始指针
  4. 都没有提供指针运算操作

shared_ptr不但有正常的赋值与拷贝语义,而且可以进行比较操作。

4.2 基本介绍

构造函数

无参数的shared_ptr创建一个持有空指针的shared_ptr。

shared_ptr(Y * p) 获得指向类型T的p指针的管理权,同时引用计数置为1。
其中类型Y必须能转化为类型T。

shared_ptr ( shared_ptr const & r), 从另一个shared_ptr获得指针的管理权,同时引用计数加1,结果是两个shared_ptr指针共享一个指针的管理权。

shared_ptr(std::auto_ptr < Y > & r),从一个auto_ptr获得指针的管理权,引用计数置为1,同时auto_ptr失去指针的管理权。

“=” 赋值运算符,对于对象shared_ptr和auto_ptr,结果同构造函数。

当然,还有特殊的shared_ptr(Y * p,D d),第二个参数指定了析构时的一些特性,之后再做介绍。

reset()

对于不带参数的reset(),效果是让引用数减1.

带参数的reset()函数在原指针引用计数减1的同事,改为管理另一个指针,效果类比同样形式的构造函数。

引用计数

unique()返回当前指针是否引用了,如果引用了,则返回true,否则,返回false,高效操作。

use_count() 较为低效率的操作,返回计数引用的指针数量,甚至有时候是不可用的。

比较运算

比较运算基于内部的指针,如a.get() == b.get().

还提供了唯一可以使用的比较运算符 " < ",因为提供了 " < " 操作,因此可以被用于某些内部的容器,比较set和map等等。

类型转换

在编写基于虚函数的多态代码时,指针的类型转换很重要,比如把一个基类指针转化为一个子类的指针,或者反过来。

但是对于shared_ptr不能使用诸如

static_cast<T*> (p.get())

的形式。
而要使用:

static_pointer_cast<T> ()
const_pointer_cast<T> ()
dynamic_pointer_cast<T> ()

分别对应标准的转型操作符:

static_cast<T> ()
const_cast<T> ()
dynamic_cast<T> ()

但以上操作符返回的类型都是shared_ptr。

4.3 使用工厂模式

#include <boost/make_shared.hpp>

make_shared()函数可以接收最多十个参数,然后把他们传递给类型T的构造函数,创建一个shared_ptr对象并返回。

make_shared()函数要比直接创建shared_ptr对象的方式更为高效,因为他内部仅仅分配了一次内存,消除了shared_ptr构造时的开销。

举个栗子

#include <vector>

#include <boost/smart_ptr.hpp>
#include <boost/make_shared.hpp>

using namespace boost;

int main()
{
	shared_ptr<string> sp = make_shared<string>("Hello make_shared make me.");
	shared_ptr<std::vector<int> > sp_vector = make_shared<std::vector<int> > (10, 2);
	assert(spv->size() == 10);
}

4.4 使用桥接模式

简要介绍

桥接模式(bridge)是一种结构型的设计模式,把类之间的具体实现细节隐藏起来,以达到最小耦合的目的。
在具体实践中,桥接模式也被称为pimpl或者handle/body惯用法,可以将文件的依赖关系降到最小,减少编译时间,可以不利用虚函数便能实现多态。

删除器

举个栗子

shared_ptr<FILE> fp(fopen("./try.txt", 'r'), fclose);

当离开作用域时,shared_ptr自动调用fclose来关闭文件。

4.5 shared_array

用法如同scoped_ptr,可以考虑更为安全的vecto,这里不做详细介绍。

5. weak_ptr

5.1 基本介绍

协助shared_ptr使用,像旁观者一样观测资源的使用情况。

weak_ptr不影响引用计数。

5.2 获得this的shared_ptr

获取this指针的shared_ptr,使对象产生shared_ptr对象管理自己。

对象使用weak_ptr观测this指针,不影响计数,在需要的时候调用lock()函数返回shared_ptr供外界使用。

简要介绍如上,如果要深入了解的话,可以继续翻阅书籍的相关章节。

6. intrusive_ptr

6.1 基本介绍

侵入式的引用计数型指针,用于如下场景:

  1. 对内存占用的要求非常严格,要求与原指针一样。
  2. 现存代码已经有了引用计数机制的对象

应用较少,只做简要介绍,如果需要的时候,再去深入学习。

 类似资料: