代码改变世界

Effective C++ 学习笔记

2011-07-27 21:20  Clingingboy  阅读(648)  评论(0编辑  收藏  举报

 

基于此文档

http://wenku.baidu.com/view/ef989106e87101f69e3195db.html

 

条款13:以对象管理资源

目标:为确保资源被释放

1.获得资源后立即放进管理对象

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

做法:

之前

void f()
{
    Investment *pInv = createInvestment();         // call factory function
    ...                                            // use pInv
    delete pInv;                                   // release object
}

之后

void f()
{
    std::auto_ptr<Investment> pInv(createInvestment());  
}

关于std::auto_ptr和std::tr1::shared_ptr用法不再叙述

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

class Lock {
public:
    explicit Lock(Mutex *pm)
        : mutexPtr(pm)

    { lock(mutexPtr); }                          // acquire resource

    ~Lock() { unlock(mutexPtr); }                // release resource

private:
    Mutex *mutexPtr;

};

coping行为

Lock ml1(&m);                      // lock m

Lock ml2(ml1); 

问题:“当一个RAII对象被复制,会发生什么事?”

选择做法:

1.禁止复制

class Lock: private Uncopyable {            // prohibit copying — see
public:                                     // Item 6
    ...                                        // as before

};

2.(最为普遍方法)利用std::tr1::shared_ptr的删除器做引用计数(当计数为0时调用的函数,但不删除指针)

class Lock {
public:
    explicit Lock(Mutex *pm)       // init shared_ptr with the Mutex

        : mutexPtr(pm, unlock)         // to point to and the unlock func

    {                              // as the deleter
        lock(mutexPtr.get());   // see Item 15 for info on "get"

    }
private:
    std::tr1::shared_ptr<Mutex> mutexPtr;    // use shared_ptr
};   

3.深拷贝资源,但不用时要做删除动作

4.利用auto_ptr做资源拥有权的转移

条款15:在资源管理类中提供对原始资源的访问

如下代码

std::tr1::shared_ptr<Investment> pInv(createInvestment());  // from Item 13

int daysHeld(const Investment *pi);        // return number of days

int days = daysHeld(pInv);                // error!

int days = daysHeld(pInv.get());      

两种方式:

1.显示转换:get方法就是显示转换

class Font {                           // RAII class
public:
    ...                       // C API does
    FontHandle get() const { return f; }

private:
    FontHandle f;                        // the raw font resource
    ...
};
changeFontSize(f.get(), newFontSize); // explicitly convert

2.隐式转换

class Font {

public:
    ...
        operator FontHandle() const { return f; }        // implicit conversion function
    ...
};

changeFontSize(f, newFontSize);     // implicitly convert

显然各有优缺点,但显示转换不易被误用,如std: string的显示转换方法

 

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

int priority();

void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);

如何传参的问题:

重点:保证传入的指针在异常的时候可以释放资源

processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());

以上做法可能有错误,如果执行顺序如下,而priority函数发生了错误,那么内存无法释放

  1. Execute "new Widget".

  2. Call priority.

  3. Call the tr1::shared_ptr constructor.

保险的做法,在外部声明

std::tr1::shared_ptr<Widget> pw(new Widget);  // store newed object

processWidget(pw, priority());                // this call won't leak

 

第四章:设计与声明

这一章大多与设计有关

条款18:让接口容易被正确使用,不易被误用

让接口更易被理解和使用

之前:

class Date {
public:

    Date(int month, int day, int year);
    ...
};

以类代替(请勿模仿,任何事物防不胜防,看需求而定)

struct Day {            struct Month {                struct Year {

    explicit Day(int d)     explicit Month(int m)         explicit Year(int y)

        :val(d) {}              :val(m) {}                    :val(y){}



    int val;                int val;                      int val;

};                      };                            };

class Date {

public:

    Date(const Month& m, const Day& d, const Year& y);

    ...

};

Date d(30, 3, 1995);                      // error! wrong types
Date d(Day(30), Month(3), Year(1995));    // error! wrong types
Date d(Month(3), Day(30), Year(1995));    // okay, types are correct

条款 21:必须返回对象时,别妄想返回其reference

拒绝一下两种写法,c#和java的开发者一定很不习惯

const Rational& operator*(const Rational& lhs,   // warning! bad code!

    const Rational& rhs)

{
    Rational result(lhs.n * rhs.n, lhs.d * rhs.d);
    return result;
}

const Rational& operator*(const Rational& lhs,   // warning! more bad

    const Rational& rhs)   // code!

{
    Rational *result = new Rational(lhs.n * rhs.n, lhs.d * rhs.d);
    return *result;
}

必须要返回对象是,请返回一个新对象

inline const Rational operator*(const Rational& lhs, const Rational& rhs)

{
    return Rational(lhs.n * rhs.n, lhs.d * rhs.d);
}

条款 22:将成员变量声明为private

真的没什么好讲的,学过c#和java的人都知道

class AccessLevels {

public:

    ...

    int getReadOnly() const        { return readOnly; }
    void setReadWrite(int value)   { readWrite = value; }
    int getReadWrite() const       { return readWrite; }
    void setWriteOnly(int value)   { writeOnly = value; }

private:

    int noAccess;                         // no access to this int
    int readOnly;                         // read-only access to this int
    int readWrite;                        // read-write access to this int
    int writeOnly;                        // write-only access to this int

};

条款 23:宁以non-member、non-friend替换member函数

class WebBrowser {
public:
    ...

    void clearCache();
    void clearHistory();
    void removeCookies();

    //void clearBrowser(WebBrowser& wb)
    //{
    //    wb.clearCache();
    //    wb.clearHistory();
    //    wb.removeCookies();
    //}
    ...

};
void clearBrowser(WebBrowser& wb)
{
    wb.clearCache();
    wb.clearHistory();
    wb.removeCookies();
}

将便利函数放在外部,不要为类添加太多的类成员(意思就是只添加必要的),基本属于设计问题

条款 24:若所有参数皆需类型转换,请为此采用non-member函数

如下示例代码

class Rational {
public:
    Rational(int numerator = 0,        // ctor is deliberately not explicit;
        int denominator = 1);     // allows implicit int-to-Rational

    // conversions

    int numerator() const;             // accessors for numerator and

    int denominator() const;           // denominator — see Item 22

    const Rational operator*(const Rational& rhs) const;
private:
    ...
};

其提供了隐式转换功能:

Rational oneEighth(1, 8);
Rational oneHalf(1, 2);

Rational result = oneHalf * oneEighth;            // fine

result = result * oneEighth;                      // fine

更加贪婪的做法

result = oneHalf * 2;                       // fine
result = 2 * oneHalf;                       // error!

为了支持2的隐式转换,做法是实现一个non-member的操作符重载

const Rational operator*(const Rational& lhs,     // now a non-member

    const Rational& rhs)     // function

{
    return Rational(lhs.numerator() * rhs.numerator(),
        lhs.denominator() * rhs.denominator());

}
Rational oneFourth(1, 4);

Rational result;

result = oneFourth * 2;                           // fine

result = 2 * oneFourth;                           // hooray, it works!

记得是所有参数