Effective C++ Notes - 4

06 Apr 2015

3. Resourse Management

Tip 13 : Use objects to manage resources.

C++ 允许动态管理内存,若资源分配后未进行释放,将造成内存泄露。即使在程序中使用了 delete 操作,仍可能因为程序的其他异常导致释放操作未执行。

1
2
3
4
5
void func() {
    Factory* factory = new concreteFactory();
    ...    // 若抛出异常,可能会造成 delete 操作未执行
    delete factory;
}

《 C++ Primer 5th 》 12.1 介绍了 shared_ptr 、unique_ptr 和 weak_ptr,使用 shared_ptr 可以对程序进行如下修改。

1
2
3
4
void func() {
    shared_ptr<Factory*> factory(ConcreteFactory());
    ...
} // 即使函数内部抛出异常,shared_ptr 的析构函数也将自动删除 factory

关于 unique_ptr 和 weak_ptr 见书。

Tip 14 : Think carefully about copying behavior in resource-managing classes.

对带有资源的类进行拷贝时,将会改变资源的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// .h
void lock(Mutex* pm);    // 锁定互斥锁
void unlock(Mutex* pm);  // 接触互斥锁
class Lock {
public:
    explict Lock(Mutex* pm) : mutexPtr(pm) { lock(mutexPtr); }
    ~Lock() { unlock(mutexPtr); }
private:
    Mutex* mutexPtr;
};
// .cpp
int main()
{
    ...
    Lock mlock1(&m);        // lock mutex
    Lock mlock2(mlock1);    // copy mutex and don't know what would happen
    ...
}

控制带有资源的类的拷贝行为,有两种方法:

(1)禁止复制(2)使用智能指针进行管理

禁止复制可以参考条款 6,使用指针可以参考条款 13。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// prohibit copying version
class Uncopyable {
public:
    Uncopyable(const Uncopyable&) = delete;
    Uncopyable& operator=(const Uncopyable&) = delete;
};
class Lock : public Uncopyable {
};

// smart point reference-counting version
class Lock {
public:
    Lock(Mutex* pm) : mutexPtr(pm, unlock) { lock(mutexPtr.get()); }
private:
    shared_ptr<Mutex*> mutexPtr;
}
Tip 15 : Provide access to raw resources in resource-managing classes.

使用智能指针时,若函数 API 需要调用原始资源(raw resources),可以使用两种方法:

(1)显示转换(2)隐式转换

shared_ptr 和 auto_ptr 都提供了 get 成员函数,显示地返回指针对象。

1
2
3
shared_ptr<Factory*> factory(ConcreteFactory());
// int getProductId(const Factory* f);
int ProductId = getProductId(factory.get()); 

若使用自定义的类进行资源调用时,可以通过实现 get 进行显示转换,实现 operator() 进行隐式转换。

1
2
3
4
5
6
7
8
9
10
class Font {
public:
    explict Font(FontHandle f) : fh(f) {}
    ~Font() { releaseFont(f); }
    
    FontHandle get() const { return f; }      // better idea
    operator FontHandle() const { return f; } // not a good idea
private:
    FontHandle fh;
}
Tip 16 : Use the asme form in corresponding uses of new and delete.

在 new 表达式中使用了 [],在 delete 中也应该对应使用 [],反之亦然。

1
2
3
4
5
std::string* strPtr1 = new std::string;
std::string* strPtr2 = new std::string[100];

delete strPtr1;
delete [] strPtr2;

更详细的细节可参考 《 C++ Primer 5th 》 12.2。

Store newed objects in smart pointers in standalone statements.

若给一个函数传入即时动态申请的智能指针,可能将造成资源泄漏。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// .h
int priority();
void processWidget(std::shared_ptr<Widget> pw, int priority);

// .cpp
int main()
{
    // compile error.
    processWidget(new Widget, priority());
    // compile success but not safe
    processWidget(std::shared_ptr<Widget>(new Widget), priority());
    // better choice
    std::shared_ptr<Widget> pw(new Widget);
    processWidget(pw, priority());
}

© 2015 plinx