EffectiveC++(3)——资源管理-创新互联

条款13:以对象管理资源(RAII)
class Inverstment { ... };

Inverstment* createInverstment();


void f() {
    Inverstment* pInv = createInverstment();
    ...
    delete pInv;
}

某些特殊情况,例如 ... 这个区域存在return语句,delete语句不会被执行到,造成内存泄漏。使用std::shared_ptr解决此问题。

创新互联公司致力于互联网品牌建设与网络营销,包括成都网站设计、成都网站建设、外贸网站建设、SEO优化、网络推广、整站优化营销策划推广、电子商务、移动互联网营销等。创新互联公司为不同类型的客户提供良好的互联网应用定制及解决方案,创新互联公司核心团队十载专注互联网开发,积累了丰富的网站经验,为广大企业客户提供一站式企业网站建设服务,在网站建设行业内树立了良好口碑。
void f() {
    std::shared_ptrpInv1(createInverstment());
    std::shared_ptrpInv2(pInv1);
    pInv1 = pInv2;
    ...
}

shared_ptr在其析构函数内做delete而不是delete[]动作,对于array来说使用shared_ptr是个馊主意。

//馊主意,会用上错误的delete形式
std::shared_ptraps(new std::string[10]);

std::shared_ptrspi(new int[1024]);

请注意:

获得资源后立刻放进管理对象;

管理对象运用析构函数确保资源被释放;

为了防止资源泄露,请使用RAII对象,它们在构造函数中获得资源并在析构函数中释放资源;

使用shred_ptr一般是较佳选择。

条款14:在资源管理类中小心copying行为

假如我们使用C API函数处理类型为Mutex的互斥锁对象,有lock和unlock函数可用:

void lock(Mutex* pm);
void unlock(Mutex *pm);

创建一个class用来管理锁

class Lock {
public:
    explicit Lock(Mutex *pm) : mutexPtr(pm) {
        lock(mutexPtr);
    }
    
    ~Lock() {
        unlock(mutexPtr);
    }

private:
    Mutex *mutexPtr;
};

客户调用

Mutex m;
...
{
    Lock m1(&m);
}

这很好,但如果Lock对象被复制,会发生什么?

Lock ml1(&m);
Lock ml2(ml1);

大多数情况,会选择以下两种可能:

  • 禁止复制。将copying操作声明为private。
class Uncopyable {
protected:
    Uncopyable() {}
    ~Uncopyable() {}
private:
    Uncopyable(const Uncopyable &);
    Uncopyable& operator=(const Uncopyable &);
};

class Lock : private Uncopyable {
public:
    ...
};
  • 对底层资源祭出“引用计数法”。不再声明析构函数,std::shared_ptr在引用计数为0时自动调用shared_ptr的删除器。
class Lock {
public:
    explicit Lock(Mutex *pm) : mutexPtr(pm, unlock) {
        lock(mutexPtr.get());
    }

private:
    std::shared_ptrmutexPtr;
};

请记住:

复制RAII对象必须一并复制它所管理的资源;

普遍而常见的RAII classcopying行为是:抑制copying、实施引用计数法。

条款15:在资源管理类中提供对原始资源的访问
std::shared_ptrpInv(createInverstment());

int daysHeld(const Inverstment* pi);

int days = daysHeld(pInv);    //无法通过编译

daysHeld需要时Inverstment*指针,传给它的确是一个std::shared_ptr的对象。可以用两种方法达成转换:显式转换和隐式转换。

// C API
FontHandle getFont();
void releaseFont(FontHanle fh);

class Font {
public:
    explicit Font(FontHandle fh) : f(fh) { }
    ~Font() { releaseFont(f); }
    FontHandle get() const { return f; } //显示转换函数
private:
    FontHandle f;
};

void changeFontSize(FontHandle f, int newSize);
Font f(getFont());
int newFontSize;
...
changeFontSize(f.get(), newFontSize);   //调用显示转换

频繁调用时到处要求显示转换,使人们倒尽胃口。另一个方法是提供隐式转换函数。

class Font {
public:
    ...
    operator FontHandle() const { return f;}   //隐式转换函数
    ...
};

Font f(getFont());
int newFontSize;
...
chageFontSize(f, newFontSize);         //调用隐式转换

这个隐式转换增加错误发生机会。如客户可能会在需要Font的时候意外创建了一个FontHandle:

Font f1(getFont());
...
FontHandle f2 = f1; //原意是要拷贝一个Font,现在却将f1隐式转换成FontHandle后复制它

上面程序有个FontHandle由Font对象f1管理,但那个FontHandle也可以通过直接使用f2获得。当f1被销毁时,字体被释放,而f2因此成为野指针。

请记住:

APIs往往是访问原始资源 ,所以每一个RAII class应该提供一个“获取其所管理之资源”的办法。

对原始资源的访问可能经由显示转换或隐式转换,一般而言显式转换比较安全,但是隐式转换对客户比较方便。

条款16:成对使用new和delete时要采取相同形式 
std::string* stringArray = new std::string[100];
...
delete stringArray;

当使用new的时候,有两个事情会发生。第一,内存被分配出来;第二,针对此内存会有一个(或更多)的构造函数被调用。当使用delete的时候,也有两件事发生。第一,针对此内存的一个(或更多)的析构函数被调用;第二,内存被释放。delete的大问题在于:即将被删除的内存之内究竟有多少对象?换句话说就是即将被删除的指针是单一对象还是对象数组。

std::string* stringPtr1 = new std::string;
std::string* stringPtr2 = new std::string[100];
...
delete stringPtr1;
delete []stringPtr2;

请记住:

如果在new表达式中使用[],必须在相应的delete表达式中使用[]。

如果在new表达式中不使用[],一定不要在相应的delete表达式中使用[]。

条款17:以独立的语句将newed对象置入智能指针

假设有两个函数

//处理程序的优先权
int priority();

//在动态分配的Widget上进行带有优先权的处理   
void processWidget(std::shared_ptrpw, int priority);

processWidget(new Widget, priority());   
//无法通过编译,参数需要shared_ptr对象, Widget构造函数是个expliclit的,无法进行隐式转换

processWidget(std::shared_ptr(new Widget), priority());

于是在调用processWidget函数之前,编译器必须创建代码,做一下三件事:调用priority函数,执行new Widget,调用std::shared_ptr构造函数。C++编译器以什么样的次序完成这几件事呢?可以肯定的是new Widget一定执行于std::shared_ptr构造函数前,但priority函数则可以排在第二或第三执行。如果编译器选择以第二顺位执行它,万一priority函数发生异常,new Widget返回的指针将会遗失,因为它尚未被置入std::shared_ptr内,可能引发内存泄漏。通常解决办法如下:

std::shared_ptrpw(new Widget);
processWidget(pw, priority());

请记住:

以独立的语句将newed对象存储于(置入)智能指针内。如果不这样做,一旦有异常被抛出,有可能导致难以察觉的资源泄漏。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


当前标题:EffectiveC++(3)——资源管理-创新互联
转载注明:http://hbruida.cn/article/djecio.html