Effective C++

【Effective C++(2)】构造/析构/赋值运算

2017-12-26  本文已影响18人  downdemo

05 了解C++默默编写并调用哪些函数

class A {};
// 相当于写了下列代码
class A {
public:
    A() { ... }
    A(const A& rhs) { ... }
    ~A() { ... }
    A& operator=(const A& rhs) { ... }
};
// 只有当这些函数被调用才会被编译器创建
A a1; // default构造函数
A a2(a1); // copy构造函数
a2 = a1; // copy assignment操作符
template<typename T>
class NameObject {
public:
    NameObject(const char* name, const T& value);
    NameObject(const std::string& name, const T& value);
    ...
private:
    std::string nameValue;
    T objectValue;
};

NameObject<int> no1("Smallest Prime Number", 2);
NameObject no2(no1); // copy构造函数
template<typename T>
class NameObject {
public:
    // 构造函数不再接受const,因为nameValue变成了reference to non-const string
    NameObject(std::string& name, const T& value);
    ...
private:
    std::string& nameValue; // 如今是reference
    const T objectValue; // 如今是const
};

std::string newDog("Persephone");
std::string oldDog("Satch");
NameObject<int> p(newDog, 2);
NameObject<int> s(oldDog, 36);
p = s; // 这步编译器会拒绝生成
// 思考下列代码就明白了
string s1("hello");
string s2("world");
string& rs1 = s1;
string& rs2 = s2;
string* ps = &s1;
rs1 = rs2;
cout << *ps; // 打印出"world",s1此时变成了s2,ps指的内容被影响了

06 若不想使用编译器自动生成的函数,就该明确拒绝

class A {
public:
    ...
private:
    ...
    A(const A&); // 只有声明
    A& operator=(const A&);
};
class Uncopyable{
protected:
    Uncopyable() {}
    ~Uncopyable() {}
private:
    Uncopyable(const Uncopyable&);
    Uncopyable& operator=(const Uncopyable&);
};

class A : private Uncopyable { // class不再声明copy构造函数或copy assignment操作符
    ...
};
struct NoCopy
{
    NoCopy() = default;
    // 和=default不同,=delete必须出现在函数第一次声明的时候
    NoCopy(const NoCopy&) = delete; // 阻止拷贝
    NoCopy& operator=(const NoCopy&) = delete; // 阻止赋值
    ~NoCopy() = default;
    // ...
}

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

class TimerKeeper {
public:
    TimeKeeper();
    ~TimeKeeper();
    ...
};
// 设计一个计时器,派生一些不同计时方法的类,如原子钟、水钟、腕表
// 然后设置一个factory函数返回指向一个派生类对象的基类指针
TimeKeeper* getTimeKeeper(); // 为了遵守factory函数的规矩,返回对象应该在heap上
TimeKeeper* ptk = getTimeKeeper();
// 避免内存泄漏,delete
delete ptk; // 只能局部销毁
class TimerKeeper {
public:
    TimeKeeper();
    virtual ~TimeKeeper();
    ...
};
TimeKeeper* ptk = getTimeKeeper();
delete ptk;

08 别让异常逃离析构函数

class A {
public:
    ...
    ~A() { ... } // 假设这里可能吐出一个异常
};
void dosomething()
{
    std::vector<A> v;
    ...
} // v在这里被自动销毁
// 假设用一个class负责数据库连接
class DBConnection {
public:
    ...
    static DBConnection create(); // 为求简化暂略参数
    void close();
};
// 为了保证客户不忘记调用close,创建一个管理DBConnection的class
class DBConn {
public:
    ...
    ~DBConn { db.close(); }
private:
    DBConnection db;
};
// 这允许客户写出下面的代码
{
    DBConn dbc(DBConnection::create());
    ...
}
// 一个方法是调用abort,如果抛出异常就结束程序
DBConn::~DBConn()
{
    try { db.close(); }
    catch(...) {
        // 制作运转记录,记录对close的调用失败
        std::abort();
    }
}
// 另一个是吞下异常,但这通常也意味着吞掉了发生错误的信息
DBConn::~DBConn()
{
    try { db.close(); }
    catch(...) {
        // 制作运转记录,记录对close的调用失败
    }
}
class DBConn {
public:
    ...
    void close()
    {
        db.close();
        closed = true;
    }
    ~DBConn()
    {
        if(!closed) {
            try { // 关闭连接,如果客户不那么做的话
                db.close();
            }
            catch(...) {
                //制作运转记录,记录对close的调用失败
                // ...
            }
        }
    }
private:
    DBConnection db;
    bool closed;
};

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

class A {
public:
    A(...);
    virtual void f() const = 0;
    ...
};
A::A(...)
{
    ...
    f(...);
}
class B : public A {
public:
    virtual void f(...) const;
    ...
};
B b; // 此步会发生问题
class A {
public:
    A(...);
    void f(...) const; // 现在不是virtual函数
    ...
};
A::A(...)
{
    ...
    f(...);
}
class B : public A {
public:
    B(...) : A(f2(...)) {}
    ...
private:
    static ... f2(...);
};

10 令operator=返回一个reference to *this

class A {
public:
    ...
    A& operator=(const A& rhs)
    {
        ...
        return *this;
    }
    ...
};

11 在operator=中处理“自我赋值”

a[i] = a[j]; // 潜在的自我赋值
*px = *py;
class A { ... };
class B {
    ...
private:
    A* p;
};

B& B::operator=(const B& rhs)
{
    delete p;
    p = new A(*rhs.p); // *this和rhs可能是同一对象
    return *this;
}
B& B::operator=(const B& rhs)
{
    if(this == &rhs) return *this;
    delete p;
    p = new A(*rhs.p); // *this和rhs可能是同一对象
    return *this;
}
B& B::operator=(const B& rhs)
{
    A* orip = p;
    p = new A(*rhs.p); // *this和rhs可能是同一对象
    delete orip;
    return *this;
}
B& B::operator=(const B& rhs)
{
    A* newp = new A(*rhs.p);
    delete p;
    p = newp;
    return *this;
}
class B {
    ...
    void swap(B& rhs);
    ...
};

void B::swap(B& rhs)
{
    using std::swap;
    swap(p, rhs.p);
}

B& B::operator=(const B& rhs)
{
    B tmp(rhs); // 调用拷贝构造函数
    swap(tmp); // 将*this数据和上述复件的数据交换
    return *this;
} // 析构tmp
B& B::operator=(const B& rhs)
{
    swap(rhs); // rhs绑定的对象现在被交换了
    return *this;
}

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

上一篇下一篇

猜你喜欢

热点阅读