EC笔记:第3部分:15、对原始资源的访问

    使用对象来管理资源,可以避免因个人疏忽带来的一些低级错误,但是不是每件事都是称心如意的。

    一些函数依然使用原始的资源对象,那么我们就需要为这些函数提供一个接口,让他们可以获取到原始对象。

    继续拿13节的智能指针说事,先上代码:

//SFAutoPtr.h

#pragma once

template<typename T>

class SFAutoPtr {

private:

T* pointer; //对象指针

size_t *ref_count; //引用计数

void dec() { //减少引用计数

if(*ref_count == 0) { //如果当前引用计数为0,则应该释放资源

delete pointer;

pointer = nullptr;

delete ref_count;

ref_count = nullptr;

return;

}

--*ref_count;

}

void inc() { //增加引用计数

++*ref_count;

}

public:

SFAutoPtr() : //默认构造函数,生成一个指针

pointer(new T),

ref_count(new size_t(0)) {}

 

template<typename ... Init_Type>

SFAutoPtr(Init_Type...args) : //带参数的构造函数,对象带有指针

pointer(new T(args...)),

ref_count(new size_t(0)) {}

 

SFAutoPtr(const SFAutoPtr<T>& other) { //拷贝构造函数,增加引用计数

pointer = other.pointer;

ref_count = other.ref_count;

inc();

}

 

bool operator==(const SFAutoPtr<T>& other) const{ //等于操作符,判断指针是否相等,这时候不应该比较ref_count

return pointer == other.pointer;

}

 

const SFAutoPtr<T>& operator=(const SFAutoPtr<T>& other) { //赋值运算符,需要将当前引用计数-1,赋值的引用计数+1

if(this == &other)

return *this;

dec();

pointer = other.pointer;

ref_count = other.ref_count;

inc();

return *this;

}

 

T operator*(int) { //解引用运算符

return *pointer;

}

 

operator T*() { //指针运算符,适用于使用指针作为参数的函数

return pointer;

}

 

T* operator->() { //成员访问操作符

return pointer;

}

 

~SFAutoPtr() { //析构函数,需要将引用计数-1

dec();

}

};

 

注意其中的:

operator T*() { //指针运算符,适用于使用指针作为参数的函数

return pointer;

}

这里就是为需要T*类型作为参数的函数提供接口,如下:

#include <iostream>

using namespace std;

 

void func(int *){

    cout<<"hello world"<<endl;

}

 

int main(){

    SFAutoPtr<int> t;

    func(t);

}

 

SFAutoPtr<int>能够很好地适应int*类型的参数,这为编码提供了不少方便,但是随之也带来了一些安全隐患:

例如,有人可能写出下面这样的代码(没错,就是小明!!!):

#include <iostream>

using namespace std;

 

int *p;

 

void setP(){

    p=SFAutoPtr<int>(5);

}

 

int main(){

    setP();

    cout<<*p<<endl;

}

 

这里不会输出5,而是会输出一个随机值,甚至程序崩溃,因为我们使用了对象管理资源,对象在销毁时,资源也会被释放。导致这一现象的原因是AFAutoPtr<int>被隐式转换为了int*,避免这种调用的方法之一就是,不再提供隐式转换,确保编写出的每一个转换都是程序员想要的,使用get()而非operator T*,将原实现中的operator T*操作符替换为get()函数。有人可能要问,如果程序员非得写出:

#include <iostream>

using namespace std;

 

int *p;

 

void setP(){

    SFAutoPtr<int> tmp(5);

    p=tmp.get();

}

 

int main(){

    setP();

    cout<<*p<<endl;

}

这样的代码怎么办?

对此我只能说:自作孽,不可活~~~

 

一般使用对象管理资源,都会提供接口访问原始对象。是使用隐式访问还是显式访问,这主要看实际需求,是为程序员带来便捷还是给程序员带来安全,你说了算~~~

posted @ 2016-12-23 23:15  SkyFireITDIY  阅读(335)  评论(0编辑  收藏  举报