12.1 动态内存与智能指针
12.1.1 shared_ptr类
-
shared_ptr<string>p1;
//可以指向string
-
shared_ptr<list<
int>>p2
//可以指向int的list
-
//如果p1不为空,检查它是否指向一个空string
-
if(p1&&pq->
empty())
-
*p1 =
"hi";
//如果p1指向一个空string,解引用p1,将一个新至赋予string
shared_ptr和unique_ptr都支持的操作见表12.1
make_shared函数
-
-
//指向一个值为42的int的shared_ptr
-
shared_ptr<
int> p3 =
make_shared<
int>(
42);
-
-
//指向一个值为9999999999的string
-
shared_ptr<string> p4 =
make_shared<string>(
10,
'9');
-
-
//指向一个值初始化的int,即,值为0
-
shared_ptr<
int> p5 =
make_shared<
int>();
-
-
//指向一个动态分配的空vector<string>
-
auto p6 = make_shared<vector<string>>();
shared_ptr的拷贝和赋值
-
auto r =
make_shared<
int>(
42);
//r指向的int只有一个引用者
-
r = q;
//给r赋值,令它指向另一个地址
-
//递增q指向的对象的引用计数
-
//递减r原来指向的对象的引用计数
-
//r原来指向的对象已没有引用者,会自动释放
shared_ptr自动销毁所管理的对象
略(析构函数)
......shared_ptr还会自动释放相关联的内存
-
//factory返回一个shared_ptr,指向一个动态分配的对象
-
shared_ptr<Foo> factory(T arg)
-
{
-
//恰当处理arg
-
//shared_ptr负责释放内存
-
return
make_shared<Foo>(arg);
-
}
-
-
//下面函数将factory返回的shared_ptr保存在局部变量中
-
void use_factory(T arg)
-
{
-
shared_ptr<Foo> p =
factory(arg);
-
//使用o
-
}
//p离开了作用域,它指向的内存会被自动释放
-
-
//但如果有如果有其他shared_ptr也指向这块内存,它就不会被释放掉
-
shared_ptr<Foo> use_factory(T arg)
-
{
-
shared_ptr<Foo> p =
factory(arg);
-
//使用p
-
return p;
//当我们返回P时,引用计数进行了递增操作
-
}
//p离开了作用域,但它指向的内存不会被释放掉
使用了动态生存期的资源的类
-
//当我们拷贝一个vector时,原vector和副本vector中的元素是相互分离的
-
vector<string>v1;
//空vector
-
{
-
vector<string>v2 = {
"a",
"an",
"the"};
-
v1 = v2;
//从v2拷贝元素到v1中
-
}
//v2被销毁,其中的元素也被销毁,v1有三个元素,是原来v2中元素的拷贝
-
-
//一般而言,如果两个对象共享底层的数据,某个对象被销毁时,我们不能单方面地销毁底层数据
-
Blob<string> b1;
//空Blob
-
{
-
//新作用域
-
Blob<string> b2 = {
"a",
"an",
"the"};
-
b1 = b2;
//b1和b2共享相同地元素
-
}
//b2被销毁了,但b2中的元素不能销毁
-
//b1指向最初由b2创建的元素
定义StrBlob类
-
class
StrBlob
-
{
-
public:
-
typedef std::vector<std::string>::size_type size_type;
-
StrBlob();
-
StrBlob(std::initializer_list<std::string>il);
-
size_type size() const {
return data->
size();}
-
bool empty() const {
return data->
empty();}
-
//添加和删除元素
-
void push_back(const std::string &t) { data->
push_back(t);}
-
void pop_back();
-
//元素访问
-
std::string& front();
-
std::string& back();
-
private:
-
std::shared_ptr<std::vector<std::string>>data;
-
//如果data[i]不合法,抛出一个异常
-
void check(size_type i,const std::string &msg) const;
-
};
-
//该类中有一个默认构造函数和一个构造函数,接受单一的initializer_list<string>类型参数
-
//此构造函数可以接受一个初始化器的花括号列表
StrBlob构造函数
-
-
StrBlob::
StrBlob():
data(make_shared<vector<string>>()){ }
-
StrBlob::
StrBlob(initializer_list<string>il):
-
data(make_shared<vector<srting>>(il)){ }
元素访问成员函数
-
//check函数检查一个给定索引是否在合法范围内
-
//check函数还会接受一个string参数,它会将此参数传递给异常处理程序,该string描述了错误内容
-
void StrBlob::check(size_type i,const string &msg)const
-
{
-
if(i>=data->
size())
-
throw
out_of_range(msg);
-
}
-
-
//pop_back和元素访问函数首先调用check,如果check成功,这些成员函数继续利用底层vector的操作来完成自己的工作
-
string& StrBlob::front()
-
{
-
//如果vector为空,check会抛出一个异常
-
check(
0,
" front on empty StrBlob");
-
return data->
front();
-
}
-
string& StrBlob::back()
-
{
-
check(
0,
"back on empty StrBlob");
-
return data->
back();
-
}
-
void StrBlob::pop_back()
-
{
-
check(
0,
"pop_back on empty StrBlob");
-
data->
pop_back();
-
}
StrBlob的拷贝,赋值和销毁
略(详见书本)
12.1.2直接内存管理
使用new动态分配和初始化对象
-
int *pi =
new
int;
//pi指向一个动态分配的、未初始化的无名对象
-
-
string *ps =
new string;
//初始化为空string
-
int *pi =
new
int;
// pi指向一个未初始化的int
-
-
int *pi =
new
int(
1024);
//pi指向的对象的值为1024
-
string *ps =
new
string(
10,
'9');
//ps为9999999999
-
//vector有10个元素,值依次从0到9
-
vector<
int> *pv =
new vector<
int>{
0,
1,
2,
3,
4,
5,
6,
7,
8,
9};
-
-
string *ps1 =
new string;
//默认初始化为空string
-
string *ps =
new
string();
//值初始化啊为空string
-
int *pi1 =
new
int;
//默认初始化:*pi1的值未定义
-
int *pi2 =
new
int();
//值初始化为0;*pi2为0
-
-
//若提供一个括号包围的初始化器,就可以使用auto。从此初始化器来推断我们想要分配的对象的类型。
-
//但是,由于编译器要用初始化器的类型来推断要分配的类型,只有当括号中仅有单一初始化器时才可使用auto
-
auto p1 =
new
auto(obj);
//p指向一个与obj类型相同的对象
-
//该对象用obj进行初始化
-
auto p2 =
new
auto{a,b,c};
//错误:括号中只能有蛋哥初始化器
动态分配的const对象
-
//用new分配const对象是合法的
-
//分配并初始化一个const int
-
const
int *pci =
new
const
int(
1024);
-
//分配并默认初始化一个const的空string
-
const string *pcs =
new
const string;
-
//对于一个定义了默认构造函数的类类型,其const动态对象可以隐式初始化,而其他类型的对象就必须显示初始化
内存耗尽(定位new的使用)
-
-
//如果分配失败,new返沪一个空指针
-
int *p1 =
new
int;
//如果分配失败,new抛出std::bad_alloc
-
int *p2 =
new (nothrow)
int;
//如果分配失败,new返回一个空指针
释放动态内存
-
-
delete p;
//p必须指向一个动态内存分配的对象或是一个空指针
指针值和delete
-
int i,*pi1 = &i,*pi2 =
nullptr;
-
double *pd =
new
double(
33),*pd2 = pd;
-
delete i;
//错误,i不是一个指针
-
delete pi1;
//未定义:pi1指向一个局部变量
-
delete pd;
//正确
-
delete pd2;
//未定义:pd2指向的内存已经被释放了
-
delete pi2;
//正确:释放一个空指针总是没有错误的
-
-
//虽然一个const对象的值不能被改变,但其本身是可以被销毁的。如同其他动态对象一样,想要释放一个const
-
//动态内存对象,只要delete指向它的指针即可
-
const itn *pci =
new
const
int(
1024);
-
delete pci;
//正确:释放一个const对象
动态对象的生存期直到被释放时为止
-
//factory返回一个指针,指向一个动态分配的对象
-
Foo* factory(T arg)
-
{
-
//看情况处理arg
-
return
new
Foo(arg);
//调用者负责释放此内存
-
}
-
-
//下面函数调用factoty,后者分配一个类型为Foo的新对象。当use_factory返回时,局部变量p被销毁
-
//此变量是一个内置指针,而而不是一个智能指针
-
void use_factory(T arg)
-
{
-
Foo *p =
factory(arg);
-
//使用p但不delete它
-
}
//p离开了它的作用域,但它所指向的内存没有被释放
-
-
-
//下面函数中p时指向factory分配的内存的唯一指针,一旦use_factory返回,程序就没有办法释放这块内存了。
-
//根据整个程序的逻辑,修正这个错误的正确方式是在use_factory中记得释放内存
-
void use_factory(T arg)
-
{
-
Foo *p =
factory(arg);
-
//使用p
-
delete p;
//现在记得释放内存,我们已经不需要它了
-
}
-
-
//还有有一种可能,我们的系统中的其他代码要使用use_factory所分配的对象,我们就应该修改此函数
-
//让其返回以一个指针,指向它分配的内存
-
Foo* use_factory(T arg)
-
{
-
Foo *p =
factory(arg);
-
//使用p
-
return p;
//调用者必须释放内存
-
}
使用new和delete管理动态内存存在三个常见问题
1.忘记delete内存
2.使用已经释放掉的对象
3.同一个块内存释放两次
delete之后重置指针值......
空悬指针的概念及避免空悬指针的方式:在指针即将要离开其作用域之前释放掉它所关联的内存
......这只是提供了有限的保护
-
int *p(new int(42));
//p指向动态内存
-
auto q = p;
//p和q指向相同的内存
-
delete p;
//p和q均变为无效
-
p =
nullptr
//指出p不再绑定到任何对象
12.1.3 shared_ptr和new结合使用
-
//new返回的指针来初始化智能指针
-
shared_ptr<
double>p1;
//shared_ptr可以指向一个double
-
shared_ptr<
int>
p2(
new
int(
42));
//p2指向一个值为42的int
-
-
//接受指针参数的智能指针构造函数时explicit的,因此不能将一个内置指针隐式转化为一个
-
//智能指针,必须使用直接初始化形式来初始化一个智能指针
-
shared_ptr<
int>p1 =
new
int(
1024);
//错误:必须使用直接初始化形式
-
shared_ptr<
int>
p2(
new
int(
1024));
//正确:使用了直接初始化形式
-
-
//由于不能进行内置指针到智能指针之间的隐式转换,一个返回shared_ptr的函数不能在
-
//其返回语句中隐式转换一个普通指针
-
shared_ptr<int> clone(int p)
-
{
-
return
new
int(p);
//错误:隐式转换为shared_ptr<int>
-
}
-
-
//我们必须将shared_ptr显示绑定到一个想要返回的指针上
-
shared_ptr<
int>
clone(
int p)
-
{
-
//正确:显示地用int*创建shared_ptr<int>
-
return
shared_ptr<
int>(
new
int(p));
-
}
不要混合使用普通指针和智能指针
-
//考虑下面对shared_ptr进行操作地函数
-
//在函数被调用时ptr被创建并初始化
-
void process(shared_ptr<int>ptr)
-
{
-
//使用ptr
-
}
//ptr离开作用域,被销毁
-
-
//此函数的正确方法是传递给它一个shared_ptr
-
shared_ptr<
int>
p(
new
int(
42));
//引用计数为1
-
process(p);
//拷贝p会递增它的引用计数;在process中引用计数值为2
-
int i = *p;
//正确:引用计数为1
-
-
int *x(new int(1024));
//危险:x是一个普通指针,不是一个智能指针
-
process(x);
//错误:不能将int*转换为一个shared_ptr<int>
-
process(
shared_ptr<
int>(x));
//合法的,但内存会被释放
-
int j = *x;
//未定义的:x是一个空悬指针
使用一个内置指针来访问一个智能指针所负责的对象是很危险的,因为我们无法知道对象何时会被销毁
......也不要使用get初始化另一个智能指针或为智能指针赋值
-
shared_ptr<
int>
p(
new
int(
42));
//引用计数为1
-
int *q = p.
get();
//正确:但使用q时要注意,不要让它管理的指针被释放
-
{
-
//新程序块
-
//未定义:两个独立的shared_ptr指向相同的内存
-
shared_ptr<
int>(q);
-
}
//程序块结束,q被销毁,它指向的内存被释放
-
int foo = *p;
//未定义;p指向的内存已经被释放了
其他shared_ptr操作
-
//可以用reset来将一个新的指针赋予一个shared_ptr
-
p =
new
int(
1024);
//错误:不能将一个指针赋予shared_ptr
-
p.
reset(
new
int(
1024));
//正确:p指向一个新对象
-
-
if(!p.
unique())
-
p.
reset(
new
string(*p));
//我们不是唯一用户;分配新的拷贝
-
*p += newVal;
//现在我们知道自己是唯一的用户,可以改变对象的值
12.1.4 智能指针和异常
-
-
//如果使用智能指针。即使程序过早结束,智能指针类也能确保在内存不再需要是将其释放
-
void f()
-
{
-
shared_ptr<int> sp(new int(42));
// 分配一个新对象
-
//这段代码抛出一个异常,且在f中为未被捕获
-
}
//这段函数结束时shared_ptr自动释放内存
-
-
//如果使用内置指针管理内存,且在new之后对应的delete之前发生了异常,则内存不会被释放
-
void f()
-
{
-
int *ip =
new
int(
42);
//动态分配一个新对象
-
//这段代码抛出一个异常,且在f中未被捕获
-
delete ip;
//在退出 之前释放内存
-
}
智能指针和哑类
-
-
//假定我们正在使用一个C和C++都使用的网络库,使用这个库的代码可能是这样的
-
struct
destination;
//表示我们正在链接什么
-
struct
connection;
//使用链接所需的信息
-
connection connect(destination*);
//打开链接
-
void disconnect(connection);
//关闭给定的链接
-
void f(destination &d/*其他参数*/)
-
{
-
//获得一个链接;记住使用完后要关闭它
-
connection c =
connect(&d);
-
//使用链接
-
//如果我们在f退出前忘记调用disconnect,就无法关闭c了
-
}
使用我们自己的释放操作
-
//为了用shared_ptr来管理一个connection 我们必须首先定义一个函数来代替delete
-
//这个删除器函数必须能够完成对shared_ptr中保存的指针进行释放的操作
-
//在本例中,我们的删除器必须接受单个类型为connection*的参数
-
void end_connection(connection *p){
disconnect(*p);}
-
-
//当我们创建一个shared_ptr时,可以传递一个(可选的)指向删除器函数的参数
-
void f(destination &d/*其他参数*/)
-
{
-
connection c =
connect(&d);
-
shared_ptr<connection>
p(&c,end_connection);
-
//使用链接
-
//当f退出时(即使是由于异常而退出),connection会被正确关闭
-
}
12.1.5unique_ptr
12.2 动态数组
12.3 使用标准库:文本查询程序
小结
转载:https://blog.csdn.net/m0_61548909/article/details/125610578
查看评论