Effective C++ 读书笔记二

Constructors, Destructors, operator=

Item5:四大默认

C++编译器默认提供了三个构造函数和一个析构函数

class Empty {
public:
    Empty() { ... } // default constructor
    Empty(const Empty& rhs) { ... } // copy constructor
    ~Empty() { ... } // destructor
    Empty& operator=(const Empty& rhs) { ... } // copy assignment operator
};

Item6:拒绝复制

boost库有个noncopyable类,类似下面的类,作为想要拒绝复制的类的基类

class Uncopyable {
protected: // allow construction
    Uncopyable() {} // and destruction of
    ~Uncopyable() {} // derived objects...
private:
    Uncopyable(const Uncopyable&); // ...but prevent copying
    Uncopyable& operator=(const Uncopyable&);
};

Item7:为多态基类声明virtual析构函数

polymorphic base classes实如其名,目的为了子类对象在面向接口编程时的正确释放。

Item8:别让异常逃离析构函数

新规范析构函数默认noexcept(true),若抛出异常,就直接terminate。书里主要是说不明确行为,有俩异常时也会terminate。

class DBConn {
public:
    ...
    void close() // new function for client use
    { 
        db.close();
        closed = true;
    }
    ~DBConn()
    {
        if (!closed) {
            try { // close the connection
                db.close(); // if the client didn’t
            }
            catch (...) { // if closing fails, note that and terminate or swallow
                make log entry that call to close failed;  
                ...  
            }
        }
    }
private:
    DBConnection db;
    bool closed;
};

Item9:绝不在构造和析构过程中调用virtual函数

由于构造函数和析构函数调用顺序,所以在基类构造和析构时,虚构函数并不会下降到子类(既然虚构理论上就是想让其为基类)。

During base class construction, virtual functions never go down into derived classes. 

Item10:令operator= 返回一个reference to *this

保持正常逻辑可行,是好习惯。

Item11:在operator= 里处理“自我赋值”

在自我赋值的道路上要考虑异常安全,当然copy and swap 和 pass by value更简洁。(C++ primer里也有讲)

Widget& Widget::operator=(const Widget& rhs) // copy and swap
{
    Widget temp(rhs); 
    swap(temp);
    return *this;
}

Widget& Widget::operator=(Widget rhs) // pass by value
{ 
    swap(rhs);
    return *this;
}

Item12:复制对象时勿忘其每一个成分

实现一个copying function(如拷贝或赋值构造函数)时,

(1) copy all local data members and 
(2) invoke the appropriate copying function in all base classes, too. //调用基类的copying函数

不要尝试去实现一个copying函数调用另一个,应该把共同机能放到第三个函数中然后去调用(init函数)。

Resource Management

Item13:以对象管理资源

RAII(Resource Acquisition Is Initialization)
智能指针(C++11已全部成了C++特性了)
auto_ptr shared_ptr 以及boost库的 boost::scoped_array boost::shared_array

Item14:在资源管理类中小心coping行为

这是上一条款的后续,常见行为:

禁止复制(如muduo网络库里的MutexLockGuard)
引用计数(shared_ptr)
深度拷贝
转移资源拥有权(auto_ptr)

class Lock { 
public:
    explicit Lock(Mutex *pm) 
    : mutexPtr(pm, unlock) // init shared_ptr with the Mutex to point to and the unlock func as the deleter
    {
        lock(mutexPtr.get());
    }
private:
    std::tr1::shared_ptr<Mutex> mutexPtr;
};

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

用户可能需要原始资源作为参数传入某个接口, 如:

class Font { 
public:
    explicit Font(FontHandle fh) 
    : f(fh)
    {}
    ~Font( ) { releaseFont(f ); }
    ...
private: 
    FontHandle f;
};

想要与下面的API通讯:
void changeFontSize(FontHandle f, int newSize);

一、提供显示调用接口:
FontHandle get() const { return f; }

二、提供隐式转换接口(参考stackoverflow):
operator FontHandle() const { return f; }

如同作者推荐单参数构造函数使用explicit禁止隐式转换一样,作者更倾向于使用显示调用接口。

Item16:成对使用new和delete时要采用相同的形式

std::string *stringPtr1 = new std::string;
std::string *stringPtr2 = new std::string[100];
...
delete stringPtr1; // delete an object
delete [] stringPtr2; // delete an array of objects

平时使用倒是不会搞错,有一点倒是容易被误会

typedef std::string AddressLines[4]; 
std::string *pal = new AddressLines; 
delete [] pal; // fine

Item17:以独立的语句将newd对象置入智能指针

下面由于执行顺序,可能会导致内存泄漏
processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());
应该像下面的样子写

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

Designs and Declarations

Item18:让接口容易被正确使用,不易被误用

面向用户设计API,限制API使用的可能性(使传参、返回值变窄)。

Item19:设计class犹如设计type

主要讲了设计一个类时需要考虑的事情(纯理论版,用到再去对照)。

Item20:以pass-by-refrence-to-const替换pass-by-value

除了内置类型、STL迭代器和函数对象。都应用pass-by-reference-to-const方式传值。

Item21:必须返回对象时,别妄想返回其reference

没啥说的,基本问题了(作者写的真多…)。

Item22:将成员变量申明为private

protected并不比public更有封装性(这条又是基本原则了,又被作者写了好多…)

Item23:宁以non-member,non-friend替换member

member函数越多越破坏封装性。在能不定义member函数也可完成功能时,选择non-member,non-friend更好。后者增加封装性、包裹弹性和机能扩充性。

Item24:若所有参数都需要类型转换,请为此采用non-member函数

譬如operator * 操作符重载时涉及了隐式类型转换。

Item25:考虑写一个不抛出异常的swap函数

std提供了swap,其实现方式如下:

//namespace std
template< typename T >
void swap(T& a, T& b)
{
T temp(a); a=b;
b = temp;
}

拷贝多次,可能存在效率问题,所以有时候需要自己实现:

class Widget {
public:
...
void swap(Widget& other)
{
using std::swap; //很重要
swap(pImpl, other.pImpl);
}
...
};

在类外模板特例化(C++ Primer pg624)

//namespace std
template<>
void swap(Widget& a, Widget& b)
{
a.swap(b);
}

当然,也可以直接类外写函数模板调用swap成员函数。

总结

回头来看,第二章在阐述如何写好每一个构造函数和析构函数。
正如第三章所说应遵从RAII方式以对象管理资源,我时常会头疼异常了怎么办,第二章的不让异常逃离析构函数让我豁然开朗。
第四章似乎围绕着封装来阐述,除了最后一条,其他看似不新鲜却非常有设计感。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值