class Investment{...}
Investment* createInvestment(); //返回指针,指向Investment继承体系内的动态分配单元
void f()
{
Investment* pInv=createInvestment(); //调用工厂函数
... //此处有return或者异常时,过早退出无法delete
delete pInv; //释放pInv所指对象
}
把资源放入对象内,便可依赖C++的析构函数自动调用机制确保资源被释放
许多资源被动态分配与堆中,而后被用于单一区块或函数内,他们应该在控制流离开那个区块或函数时被释放
auto_ptr:析构函数自动对其所指对象调用delete
注意:别让多个auto_ptr指向同一对象
void f()
{
std::auto_ptr pInv(createInvestment());
...
}
若通过复制构造函数或复制操作符复制它们,会变成null,复制所得的指针将取得资源唯一拥有权
std::auto_ptr pInv1(createInvestment()); //pInv1指向createInvestment返回物
std::auto_ptr pInv2(pInv1); //pInv2指向对象 pInv1设为null
pInv1=pInv2; //pInv1指向对象 pInv2设为null
tr1::shared_ptr:持续追踪共有多少对象指向某笔资源,并在无人指向它时自动删除该资源
void f()
{
std::tr1::shared_ptr pInv1(createInvestment());
//pInv1指向createInvestment返回物
std::tr1::shared_ptr pInv2(pInv1);//pInv1和pInv2指向同一个
pInv1=pInv2; //无任何改变
...
} //pInv1和pInv2被销毁,所指对象也被销毁
资源取得时机便是初始化时机(RAII)
class Lock{
public:
explicit Lock(Mutex* pm):mutexPtr(pm)
{
lock(mutexPtr); //获得资源
}
~lock()
{
unlock(mutexPtr); //释放资源
}
private:
Mutex *mutexPtr;
};
问题:当一个RAII对象被复制时会发生什么事??
解决:
(1)禁止复制。将复制操作声明为private(条款06)
class Lock:private Uncopyable{ //禁止复制
public:
...
};
(2)对底层资源使用“引用计数法”
只要内含一个tr1::shared_ptr成员变量,RAII class可以实现“引用计数”复制行为
tr1::shared_ptr指定“删除器”,当引用次数为0时便调用
class Lock {
public:
explicit Lock(Mutex* pm) // 以某个Mutex初始化shared_ptr
:mutexPtr(pm, unlock) // 并以unlock函数为删除器
{
lock(mutexPtr.get());
}
private:
std::tr1::shared_ptr mutexPtr; // 使用shared_ptr替换raw point
};
(3)复制底部资源
复制资源管理对象时,进行的是“深度拷贝”
“指向堆内存”的指针构成的字符串对象被复制,无论指针或是其所指向内存都会被制作出一个复制
(4)转移底部资源的拥有权
RAII对象被复制,永远只有一个RAII对象指向一个未加工资源,此时,资源拥有权会从被复制物转移到目标物
std::tr1::shared_ptr pInv(createInvestment());
// 某个函数处理Investment对象:
int daysHeld(const Investment* pi); // 返回投资天数
将RAII对象转化为所含的原始资源:
(1)显式转换
get()成员函数:返回智能指针内部原始指针:
int days = daysHeld(pInv.get());
tr1::shared_ptr和auto_ptr也重载了指针取值操作符(operator->和operator*),允许隐式转换至底部原始指针:
class Investment {
public:
bool isTaxFree() const;
...
};
Investment* createInvestment(); // factory函数
std::tr1::shared_ptr pi1(createInvestment()); // std::tr1::shared_ptr统一管理一笔资源
bool taxable1 = !(pi1->isTaxFree()); // 经由operator->访问资源
...
std::auto_ptr pi2(createInvestment()); // std::auto_ptr统一管理一笔资源
bool taxable2 = !((*pi2).isTaxFree()); // 经由operator*访问资源
...
(2)隐式转换
FontHandle getFont();
void releaseFont(FontHandle fh);
class Font {
public:
explicit Font(FontHandle fh):f(fh)
{ }
~Font()
{ releaseFont(f);}
private:
FontHandle f;
};
假设处理的是FontHandle,将Font转换为FontHandle会是一种很繁琐的要求,Font class可为此提供一个显式的转换函数,像get那样:
class Font {
public:
...
FontHandle get() const { return f; }
...
};
...
Font f(getFont());
changeFontSize(f.get(),newFontSize); // Font转化为FontHandle
另一种方法是令Font提供隐式转换函数:
class Font {
public:
...
opeartor FontHandle const
{ return f; }
...
};
...
Font f(getFont());
changeFontSize(f,newFontSize); // Font转化为FontHandle
显式转换比较安全,但隐式转换对客户比较方便
使用new,会有两件事发生:
(1)内存被分配出来(通过operator new的函数)
(2)针对此内存会有一个构造函数被调用
使用delete,会有两件事发生:
(1)针对此内存会有一个构造函数被调用
(2)内存通过operator delete的函数被释放
使用delete时加上[ ],delete认定指针指向一个数组,否则指向单一对象
std::string* stringPtr1 = new std::string;
std::string* stringPtr2 = new std::string[100];
...
delete stringPtr1; // 删除一个对象
delete [] stringPtr2; // 删除一个由对象组成的数组
int priority();
void processWidget(std::tr1::shared_ptr pw, int priority);
调用processWidget():
processWidget(Widget, priority()); // error: 不能通过编译
写成这样可以通过编译:
processWidget(std::tr1::shared_ptr(new Widget), priority()); // 可能造成内存泄漏
在调用processWidget之前,编译器创建代码,做三件事:
(1)调用priority
(2)执行new Widget
(3)调用std::tr1::shared_ptr构造函数
但调用次序不定,若先调用priority,导致异常,则new Widget返回指针遗失
避免这类问题的方法:分离语句
分别写出(1)创建Widget;(2)将它置入一个智能指针内;(3)把智能指针传给processWidget
std::tr1::shared_ptr pw(new Widget)
processWidget(pw, priority());