您的位置:首页 > 编程语言 > C语言/C++

Effective c++ 第二章总结

2016-08-20 12:01 211 查看
5.了解C++默默编写并调用哪些函数。

当你定义一个空类,c++默认会给你加上一些函数,但是惟有当这些函数被需要(被调用),它们才会被编译器创建出来。如下:

class CEmpty{};

相当于:

class CEmpty

{

public:
CEmpty(){..}
CEmpty(const CEmpty& rhs){...}
~CEmpty(){...}//编译器产出的析构函数没有virtual,除非基类析构函数带有virtual

CEmpty& operator=(const CEmpty& rhs){...}

};

至于copy构造函数和copy赋值操作符,编译器创建的版本只是单纯地将来源对象的每一个non-static成员变量拷贝到目标对象。

如果你打算在一个“内含引用成员”或者“内含const成员”的类内支持赋值操作,你必须自己定义 赋值操作符函数,更改引用和const

变量是不合法的。

如果基类将赋值操作符函数或者复制构造函数声明为private,编译器不会为派生类声明赋值操作符函数和复制构造函数。

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

如果要想让类禁用 复制构造 和 赋值操作,则必须将 “复制构造函数”和“赋值操作函数”声明为private,并且不能实现它。

例:

class HomeForSale

{

public:
...

private:
...
HomeForSale(const HomeForSale&);//只声明
HomeForSale& operator=(const HomeForSale&);//只声明

};

以上可能会报链接错误。

下面的方式不会报链接错误。

class Uncopyable

{

protected:
Uncopyable(){}
~Uncopyable(){}

private:
Uncopyable(const Uncopyable&);
Uncopyable& operator=(const Uncopyable&);

};

class HomeForSale:private Uncopyable

{...};

总结:为驳回编译器自动提供的机能,可将相应的成员函数声明为private并且不予实现,使用像Uncopyable这样的base class也是一种做法。

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

c++明确指出,当derived class 对象经由一个base class 指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义--实际

执行时通常发生的是对象的derived成分没有被销毁。于是造成一个诡异的“局部销毁”对象,形成资源泄漏,数据破坏。

任何 class 只要带有virtual函数都几乎确定应该也有一个virtual析构函数。

如果class 不含virtual函数,通常表示它并不意图被用做一个base class.

如果一个类没有虚函数,你可以把这个类当成结构体使用,但是如果有虚函数,就会有一个虚函数表指针,这样就不能单纯的当一个结构体使用了。

如果想声明一个

总结:

a.带多态性质的base classes应该声明一个virtual析构函数。如果class带有任何virtual函数,就应该拥有一个virtual析构函数。

b.classes的设计目的如果不是作为base classes使用,或不是为了具备多态性,就不该声明virtual析构函数。

8.别让异常逃离析构函数。

析构函数如何处理异常的例子:

class DBConnection

{

public:
...
static DBConnection create();//这个函数返回DBConnection对象
void close();

};

class DBConn //这个class用来管理DBConnection对象

{

public:
...
~DBConn()
//确保数据库连接总是会被管理
{
db.close();
}

private:
DBConnection db;

};

DBConn dbc(DBConnection::create());//dbc被析构会关闭数据库连接

异常处理的几种不同方式:

DBConn::~DBConn()

{
try{db.close}
catch(...)
{
...
std::abort();
}

}

DBConn::~DBConn()

{
try{db.close();}
{
...
}

}

class DBConn

{

public:
...
void close()
{
db.close();
closed=true;
}
~DBConn()
{
if(!closed)
{
try
{
db.close();
}
catch(...)
{...}
}
}

};

总结:

a.析构函数绝对不要吐出异常。如果一个被析构函数调用的函数可能抛出异常,析构函数应该捕获任何异常,然后吞下它们或结束程序。

b.如果客户需要对某个操作函数运行期间抛出异常做出反应,那么class应该提供一个普通函数(而非在析构函数中)执行该函数。

9.绝不在构造和析构过程中调用virtual函数。

总结:在构造和析构期间不要调用virtual函数,因为这类调用从不下降至derived class(比起当前执行构造函数和析构函数的那层)

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

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

Widget& Widget::operator=(const Widget& rhs)<
4000
br />
{
if(this!=&rhs)
{
delete pb;
pb=new Bitmap(*rhs.pb)

}
return *this;

}

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

任何时候只要你承担起“为derived class撰写copying函数”的重责大任,必须很小心地也复制其base class成分。

Customer为基类 PriorityCustomer为派生类。

PriorityCustomer::PriorityCustomer(const PriorityCustomer &rhs)

:Customer(rhs),

priority(rhs.priority)

{
...

}

PriorityCustomer& PriorityCustomer::operator=(const PriorityCustomer& rhs)

{
Customer::operator=(rhs);
priority=rhs.priority;
return *this;

}

总结:

a.copying函数应该确保复制“对象内的所有成员变量”及“所有base class成分”。

b.不要尝试以某个copying函数实现另外一个copying函数。应该将共同机能放进第三个函数中,并由两个copying函数共同调用。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: