网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
explicit Lock(Mutex *pm): mutexPtr(pm, unlock){
lock(mutexPtr.get()); // .get 在条款15里
}
private:
std::shared_ptr mutexPtr;
}
// Lock
的析构会引起mutexPtr
的析构,而mutexPtr
计数到0时unlock(mutexPtr.get())
会被调用。
};
3. 拷贝底层资源。当你可以任意拥有底层资源时,可以直接拷贝它。比如string的行为:内存存有指向对空间的指针,当它被复制时会复制那片空间。
4. 转移底层资源的所有权。auto\_ptr就是这样做的,把资源移交给另一个资源管理对象,自己的资源置空。
## 条款15 :在资源管理类中提供对原始资源的访问
>
> 在资源管理类中提供对原始资源的访问 (显式 隐式都搞一份)
> `Provide access to raw resources in resource-managing classes.`
>
>
>
在一个完美的设计中,所有的资源访问都应通过资源管理对象来进行,资源泄漏被完美地克服。然而世界是不完美的, 很多`API`会直接操作资源,尤其是一些`C`语言的`API`。总之,你会时不时地发现有需要直接访问资源, 所以资源管理对象需要提供对原始资源访问。获取资源的方式有两类:`隐式地获取和显式地获取`。 通常来讲,显式的资源获取会更好,它最小化了无意中进行类型转换的机会。
### 显式地获取资源
`shared_ptr`提供了`get`方法来得到资源。
shared_ptr pInv;
int daysHeld(Investment *pi);
int days = daysHeld(pInv.get()); // 通过get获取指针 执行显示装换
为了让`pInv`表现地更像一个指针,`shared_ptr`还重载了解引用运算符(`dereferencing operator`)`operator->`和`operator*`:
class Investment{
public:
bool isTaxFree() const;
};
shared_ptr pi1(createInvestment());
bool taxable1 = !(pi1->isTaxFree());
bool texable2 = !((*pi1).isTaxFree());
### 隐式地获取资源
提供`get`方法、`operator->`、`operator*`已经让资源访问很方便了。然而不幸的是,程序员是懒惰的,我们还是希望能够更加简便。 隐式转换操作符便可以完成这个工作,比如操作系统提供了`FontHandle`来操作字体:
FontHandle getFont(); // C API
void releaseFont(FontHandle fh); // CAPI
void changeFontSize(FontHandle f, int newSize);// 将 Font 隐式装换为 FontHandle
我们封装了`Font`来管理资源:
class Font{ // RAII class
private:
FontHandle f;
public:
explicit Font(FontHandle fh): f(fh){} // pass-by-value
~Font(){ releaseFont(f); };
FontHandle get() const { // 显示转换
return f;
}
};
通过`get`方法来访问`FontHandle`:
显示转换确实麻烦, 但是这样可以有效地避免资源(字节)泄漏
Font f(getFont());
int newFontSize;
changeFontSize(f.get(), newFontSize);
如果提供一个隐式类型转换运算符将`Font`转换为`FontHandle`,那么接受`FontHandle`类型作为参数的函数将会同样地接受`Font`类型。 一切将会变得简单:
class Font{
operator FontHandle() const{ // 隐式装换
return f;
}
};
Font f(getFont());
int newFontSize;
changeFontSize(f, newFontSize); // 隐式转换 为 forthandle
然而问题也随之出现:
Font f1(getFont());
FontHandle h2 = f1; // 愿意拷贝一个Font对象 但是把f1隐式装换为FontHandle
用户无意间拷贝了一份资源!该资源并未被管理起来。这将会引发意外的资源泄漏。所以隐式转换在提供便利的同时, 也引起了资源泄漏的风险。在考虑是否提供隐式转换时,需要权衡考虑资源管理类的设计意图,以及它的具体使用场景。 通常来讲,显式的资源获取会更好,它最小化了无意中进行类型转换的机会。
>
> must remenber :
>
>
>
* APIs 往往要求访问原始资源,所以没用过RAII class 应该提供一个“取得其所管理的资源”的办法
* 对原始资源的访问可能经有显示转换or隐式转换。 一般而言显示转换比较安全,但隐式转换对客户比较方便 。
## 条款16: 成对使用new 和 delete 时要采用相同形式
>
> 成对使用new 和 delete 时要采用相同形式
> Use the same form in corresponding uses of new and delete.
>
>
>
这是C++界中家喻户晓的规则:如果你用`new`申请了动态内存,请用`delete`来销毁;如果你用`new xx[`]申请了动态内存,请用`delete[]`来销毁。 不必多说了,来个例子吧:
int* p = new int[2]{11, 22};
printf(“%d, %d”, *p, *(p+1));
delete[] p;
输出是:
11, 22
如果`delete`的形式不同于`new`,则会产生未定义的行为。 因为`delete`需要调用相应的析构函数,所以它需要知道被删除的是数组还是单个对象。 即使是基本数据类型,错误的调用也会导致未定义行为。
不过在`Homebrew gcc 5.1.0`中,在int数组上调用`delete`不会引发严重后果。只是后面的动态内存未被释放而已。 但是用`delete`来删除`string`数组,会有如下错误:
malloc: *** error for object 0x7fcd93c04b38: pointer being freed was not allocated
不管怎样,只需要记住用使用同样的形式来new和delete就好了。唯一的问题在于:typedef。请看例子:
typedef string address[4]; // 每个地址是四个字符串
string* addr = new address; // 相对于使用string[4]
delete[] addr;
注意!此时用new来申请空间,却需要使用delete[]来释放。可能你会想这样写:
address* addr = new address;
delete addr;
问题在于addr的初始化语句中,等号两边的类型不兼容:
* 等号右边:`new address`的返回值与`new string[4]`具有同样的类型:string\*。
* 等号左边:`addr`的类型是数组指针:`string (*)[4]。`
最终的解决办法还是避免使用typedef来定义数组,你可以使用更加面向对象的vector、string等对象。
## 条款 17: 以独立语句将newed对象置入智能指针
>
> 以独立语句将newed对象置入智能指针
> Store newed objects in smart pointers in standalone statements.
>
>
>
在单独的语句中将`new`的对象放入智能指针,这是为了由于其他表达式抛出异常而导致的资源泄漏。 因为C++不同于其他语言,函数参数的计算顺序很大程度上决定于编译器。
如果你在做`Windows`程序设计,或者`DLL`开发,可能会经常碰到类似\_\_cdecl,\_\_stdcall等关键字。它们便是来指定参数入栈顺序的。
还是举个例子比较清晰:
processWidget(shared_ptr(new Widget), priority());
上述代码中,在`processWidget`函数被调用之前参数会首先得到计算。可以认为包括`三部分`的过程:
1. 执行`new Widget`;
2. 构造`shared_ptr<Widget>`;
3. 调用`priority()`。
多数情况下编译器有权决定这`三部分`过程的顺序,如果很不幸由于某种效率原因,编译器认为顺序应当是`1, 3, 2`,即:
1. 执行`new Widget`;
2. 调用`priority()`。
3. 构造`shared_ptr<Widget>`;
那么如果`priority`抛出了异常,新的`Widget`便永远地找不回来了。虽然我们处处使用智能指针,资源还是泄漏了!
于是更加健壮的实现中,应当将创建资源和初始化智能指针的语句独立出来:
![img](https://img-blog.csdnimg.cn/img_convert/4e3d189bd1171211c23fcca17f32e91a.png)
![img](https://img-blog.csdnimg.cn/img_convert/e55791faa1fd3ed36ea46b82f536bb6d.png)
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
[外链图片转存中...(img-OxTTDKK5-1715557857577)]
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**[需要这份系统化的资料的朋友,可以添加戳这里获取](https://bbs.csdn.net/topics/618668825)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**