shared_ptrp1; //可以指向string
shared_ptr>p2 //可以指向int的list
//如果p1不为空,检查它是否指向一个空string
if(p1&&pq->empty())
*p1 = "hi"; //如果p1指向一个空string,解引用p1,将一个新至赋予string
shared_ptr和unique_ptr都支持的操作见表12.1
//指向一个值为42的int的shared_ptr
shared_ptr p3 = make_shared(42);
//指向一个值为9999999999的string
shared_ptr p4 = make_shared(10,'9');
//指向一个值初始化的int,即,值为0
shared_ptr p5 = make_shared();
//指向一个动态分配的空vector
auto p6 = make_shared>();
auto r = make_shared(42);//r指向的int只有一个引用者
r = q; //给r赋值,令它指向另一个地址
//递增q指向的对象的引用计数
//递减r原来指向的对象的引用计数
//r原来指向的对象已没有引用者,会自动释放
略(析构函数)
//factory返回一个shared_ptr,指向一个动态分配的对象
shared_ptr factory(T arg)
{
//恰当处理arg
//shared_ptr负责释放内存
return make_shared(arg);
}
//下面函数将factory返回的shared_ptr保存在局部变量中
void use_factory(T arg)
{
shared_ptr p = factory(arg);
//使用o
}//p离开了作用域,它指向的内存会被自动释放
//但如果有如果有其他shared_ptr也指向这块内存,它就不会被释放掉
shared_ptr use_factory(T arg)
{
shared_ptr p = factory(arg);
//使用p
return p;//当我们返回P时,引用计数进行了递增操作
}//p离开了作用域,但它指向的内存不会被释放掉
//当我们拷贝一个vector时,原vector和副本vector中的元素是相互分离的
vectorv1;//空vector
{
vectorv2 = {"a","an","the"};
v1 = v2; //从v2拷贝元素到v1中
}//v2被销毁,其中的元素也被销毁,v1有三个元素,是原来v2中元素的拷贝
//一般而言,如果两个对象共享底层的数据,某个对象被销毁时,我们不能单方面地销毁底层数据
Blob b1;//空Blob
{
//新作用域
Blob b2 = {"a","an","the"};
b1 = b2; //b1和b2共享相同地元素
}//b2被销毁了,但b2中的元素不能销毁
//b1指向最初由b2创建的元素
class StrBlob
{
public:
typedef std::vector::size_type size_type;
StrBlob();
StrBlob(std::initializer_listil);
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>data;
//如果data[i]不合法,抛出一个异常
void check(size_type i,const std::string &msg) const;
};
//该类中有一个默认构造函数和一个构造函数,接受单一的initializer_list类型参数
//此构造函数可以接受一个初始化器的花括号列表
StrBlob::StrBlob(): data(make_shared>()){ }
StrBlob::StrBlob(initializer_listil):
data(make_shared>(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();
}
略(详见书本)
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 *pv = new vector{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}; //错误:括号中只能有蛋哥初始化器
//用new分配const对象是合法的
//分配并初始化一个const int
const int *pci = new const int(1024);
//分配并默认初始化一个const的空string
const string *pcs = new const string;
//对于一个定义了默认构造函数的类类型,其const动态对象可以隐式初始化,而其他类型的对象就必须显示初始化
//如果分配失败,new返沪一个空指针
int *p1 = new int;//如果分配失败,new抛出std::bad_alloc
int *p2 = new (nothrow)int; //如果分配失败,new返回一个空指针
delete p; //p必须指向一个动态内存分配的对象或是一个空指针
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.同一个块内存释放两次
空悬指针的概念及避免空悬指针的方式:在指针即将要离开其作用域之前释放掉它所关联的内存
int *p(new int(42));//p指向动态内存
auto q = p; //p和q指向相同的内存
delete p; //p和q均变为无效
p = nullptr //指出p不再绑定到任何对象
//new返回的指针来初始化智能指针
shared_ptrp1;//shared_ptr可以指向一个double
shared_ptrp2(new int(42));//p2指向一个值为42的int
//接受指针参数的智能指针构造函数时explicit的,因此不能将一个内置指针隐式转化为一个
//智能指针,必须使用直接初始化形式来初始化一个智能指针
shared_ptrp1 = new int(1024); //错误:必须使用直接初始化形式
shared_ptrp2(new int(1024)); //正确:使用了直接初始化形式
//由于不能进行内置指针到智能指针之间的隐式转换,一个返回shared_ptr的函数不能在
//其返回语句中隐式转换一个普通指针
shared_ptr clone(int p)
{
return new int(p); //错误:隐式转换为shared_ptr
}
//我们必须将shared_ptr显示绑定到一个想要返回的指针上
shared_ptrclone(int p)
{
//正确:显示地用int*创建shared_ptr
return shared_ptr(new int(p));
}
//考虑下面对shared_ptr进行操作地函数
//在函数被调用时ptr被创建并初始化
void process(shared_ptrptr)
{
//使用ptr
}//ptr离开作用域,被销毁
//此函数的正确方法是传递给它一个shared_ptr
shared_ptrp(new int(42)); //引用计数为1
process(p); //拷贝p会递增它的引用计数;在process中引用计数值为2
int i = *p; //正确:引用计数为1
int *x(new int(1024)); //危险:x是一个普通指针,不是一个智能指针
process(x); //错误:不能将int*转换为一个shared_ptr
process(shared_ptr(x)); //合法的,但内存会被释放
int j = *x; //未定义的:x是一个空悬指针
使用一个内置指针来访问一个智能指针所负责的对象是很危险的,因为我们无法知道对象何时会被销毁
shared_ptrp(new int(42)); //引用计数为1
int *q = p.get(); //正确:但使用q时要注意,不要让它管理的指针被释放
{
//新程序块
//未定义:两个独立的shared_ptr指向相同的内存
shared_ptr(q);
}//程序块结束,q被销毁,它指向的内存被释放
int foo = *p; //未定义;p指向的内存已经被释放了
//可以用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; //现在我们知道自己是唯一的用户,可以改变对象的值
//如果使用智能指针。即使程序过早结束,智能指针类也能确保在内存不再需要是将其释放
void f()
{
shared_ptr 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_ptrp(&c,end_connection);
//使用链接
//当f退出时(即使是由于异常而退出),connection会被正确关闭
}