effectiveC++文档格式.docx

上传人:b****1 文档编号:3994234 上传时间:2023-05-02 格式:DOCX 页数:29 大小:26.75KB
下载 相关 举报
effectiveC++文档格式.docx_第1页
第1页 / 共29页
effectiveC++文档格式.docx_第2页
第2页 / 共29页
effectiveC++文档格式.docx_第3页
第3页 / 共29页
effectiveC++文档格式.docx_第4页
第4页 / 共29页
effectiveC++文档格式.docx_第5页
第5页 / 共29页
effectiveC++文档格式.docx_第6页
第6页 / 共29页
effectiveC++文档格式.docx_第7页
第7页 / 共29页
effectiveC++文档格式.docx_第8页
第8页 / 共29页
effectiveC++文档格式.docx_第9页
第9页 / 共29页
effectiveC++文档格式.docx_第10页
第10页 / 共29页
effectiveC++文档格式.docx_第11页
第11页 / 共29页
effectiveC++文档格式.docx_第12页
第12页 / 共29页
effectiveC++文档格式.docx_第13页
第13页 / 共29页
effectiveC++文档格式.docx_第14页
第14页 / 共29页
effectiveC++文档格式.docx_第15页
第15页 / 共29页
effectiveC++文档格式.docx_第16页
第16页 / 共29页
effectiveC++文档格式.docx_第17页
第17页 / 共29页
effectiveC++文档格式.docx_第18页
第18页 / 共29页
effectiveC++文档格式.docx_第19页
第19页 / 共29页
effectiveC++文档格式.docx_第20页
第20页 / 共29页
亲,该文档总共29页,到这儿已超出免费预览范围,如果喜欢就下载吧!
下载资源
资源描述

effectiveC++文档格式.docx

《effectiveC++文档格式.docx》由会员分享,可在线阅读,更多相关《effectiveC++文档格式.docx(29页珍藏版)》请在冰点文库上搜索。

effectiveC++文档格式.docx

2、集成一个专门用来防止copy构造和赋值操作

classUncopyable{

protected:

Uncopyable(){}

~Uncopyable(){}

private:

Uncopyable&

operator=(constUncopyable&

Uncopyable(constUncopyable&

classA:

publicUncopyable{

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

1、

classperson{

person(){}

~person(){}

classstudent:

publicperson{

person*p=newstudent;

deletep;

此时只能删除person部分,而student剩余部分无法删除。

解决办法:

persion的析构函数设为virtual。

2、没有virtual函数的类不需要virtual析构函数,那样只会增加对象的大小。

3、当继承一个没有virtual的stl类时会产生类似于1中的情况

classMyString:

publicstring{}

MyString*pms=newMyString();

string*ps=pms;

deleteps;

//只能部分delete

NO8析构函数不要抛出异常

1、如果某个操作可能引起异常,且发生异常时必须要处理,则需将这个操作放在非析构函数中,以使得用户有时间处理他们。

NO9构造、析构函数不要调用virtual函数

构造函数和析构函数中不能调用virtual函数,因为此时构造尚未完成,所以不可能调用派生类类的方法而只能调用基类的方法。

NO10operator=返回*this

赋值可以写成连锁形式:

x=y=z=15;

因此operator=一般应该返回一个引用指向当前对象:

val(99){}

intval;

operator=(constA&

rhs){

this->

val=rhs.val;

return*this;

}

Aa,b,c;

c.val=(100);

a=b=c;

NO11在operator=中处理自我赋值

方法1:

if(this->

p==rhs.p)

classPeople{

classHouse{

House(People*_p):

p(_p){}

House&

operator=(constHouse&

if(this->

p==rhs.p){return*this;

deletep;

p=rhs.p;

People*p;

方法2:

House&

People*_p=this->

p;

p=newPeople(*rhs.p);

delete_p;

方法3:

自己写一个swap函数

NO12复制对象时需要复制每一个成分

People&

operator=(constPeople&

rhs){

age=rhs.age;

//this->

name=rhs.name;

如果遗忘了,编译器不会提醒

intage;

stringname;

classStudent:

publicPeople{

Student&

operator=(constStudent&

People:

:

operator=(rhs);

id=rhs.id;

intid;

NO13以对象管理资源

classDBConnection{

DBConnection*conn_pool(){

returnnewDBConnection;

voidfunc(){

DBConnection*conn=conn_pool();

//……如果发生异常,或者提前return则conn指向的对象无法删除

deleteconn;

2、解决办法:

voidtest(){

std:

auto_ptr<

DBConnection>

pdb(conn_pool());

pdb.get()->

do_sth();

3、注意事项

auto_ptr复制时,参数指针变成null(因为需要防止两个指针指向同一个对象)

不能作为容器元素

4、shared_ptr

可以追踪对象的引用计数,可以用来作为容器元素

5、auto_ptr和sharted_ptr析构函数内均做delete而不是delete[],因此它们不适合指向动态分配的array对象。

NO14shared_ptr删除器

classA{};

voiddel_func(A*a){

cout<

"

删¦

?

除y对?

象¨

®

A*a=newA;

shared_ptr<

A>

pa(a,del_func);

NO15在资源类中提供对原始资源的访问

1、显示转换,.get()->

*等等:

a(7){}

voiddisplay(){cout<

值¦

Ì

ê

o"

a<

inta;

pa(a);

typeid(pa.get()).name()<

pa->

display();

(*pa).a<

2、隐式转换

classDB{

DB():

name("

oracle"

){}

classDBManager{

DBManager(DB&

d):

data(d){}

operatorDB()const{

returndata;

DBget(){returndata;

private:

DBdata;

voidprint_db(DBd){

d.name<

DBd;

DBManagerdbm(d);

print_db(dbm.get());

//显示转换

print_db(dbm);

//通过重载运算符()的隐式转换

print_db(dbm.operatorDB());

//和上面一句是等价的

NO16delete与delete[]

1、删除数组指针要用delete[]

2、注意typedef导致的混淆

typedefstringAddressLines[4];

string*pal=newAddressLines;

//等价于string*pal=newstring[4];

delete[]pal

//但是如果没有看到typedf语句的话很容易把pal当成一个string的指针,从而写出了:

deletepal;

NO17以独立语句将new对象置入智能指针

classWidget{};

intget_priority(){

staticinti=0;

returni++;

voidprocessWidget(std:

tr1:

shared_ptr<

Widget>

pw,intpriority){

//dosomething;

//错误做法:

因为编译器并没有确定processWidget前后两个参数产生顺序,如果//get_priority()调用在newwidget和构造shared_ptr直接发送异常会导致内存泄漏。

//processWidget(std:

(newWidget),get_priority());

//正确做法:

把构造shared_ptr的语句放到函数调用外面。

pw(newWidget);

processWidget(pw,get_priority());

NO18让接口容易被正确使用

1、导入新类型以预防客户端错误

2、避免与内置类型不一致

eg:

重载operator*()需要加上修饰符const,以使得a*b=c不合法

3、使用shared_ptr,直接返回包装好的指针(且同时制定删除器而不是由用户对shared_ptr进行删除)

NO19略

NO20用const的引用传递代替值传递

1、值传递的函数使用的是传递过来对象的副本,所以函数不能能改变他,因此换做引用传递时必须保证是constXXX&

x.

2、引用传递还可以防止对象切割导致的问题:

virtualvoidspeak()const{cout<

iamapeople"

classChinese:

voidspeak()const{cout<

我是中国人"

//Chinesepfunc(p)只能输出"

因为根据实参构造形参时把baseclass

//以外的部分切割了。

voidfunc(Peoplep){

p.speak();

//可以正常运行

voidfunc(constPeople&

p){

当然了,通过这种方法是调用的必须是const成员函数(因为不管事值传递还是const引用传递都不可能改变原来的对象)。

3、pass-by-value成本不高的集中情况:

内置类型、STL的迭代器、函数对象

NO21必须返回对象时别妄想返回其引用

几种错误情况:

1、Studen&

get(){

Students(“name”,13);

returns;

这样返回的引用指向的student在函数返回时就已经被析构了

即使函数内设置staticStudent也有问题(多线程安全性、两次get()所得对象无法比较)

2、Student&

Student*s=newStudent(“name”,13);

这样删除Student对象变得很困难。

尤其是x*y*z这种隐藏的指针根本无法删除。

NO22将成员变量声明为private

1、提高封装性,改变实现后用户代码可以保存不变

2、其实protected和public一样会带来封装性的严重下降

NO23用非成员函数、非友元替换成员函数

1、no-member相比于member函数可以使更少的代码访问类的private成员

2、采用no-member函数,并将不同作用的non-member函数分布在不同的头文件中会有效提高扩展性:

NO24若参数均需类型转换,改用non-member函数

classRational{

Rational(intnumerator=0,intdenominator=1):

nu(numerator),de(denominator){}

intnumerator()const{returnnu;

intdenominator()const{returnde;

intnu;

intde;

若Rational中增加一个成员函数operator*:

constRationalopeartor(constRational&

returnRational(nu*rhs.nu,de*rhs.de);

则:

Rationalr4=r*2;

//正确

Rationalr5=2*r;

//错误

这是因为:

只有当参数被列于参数列时,他才是隐式转换的合格参与者,因此r*2中的2被当成了operator*(constRational&

)中的参数。

而2*r中的2不在参数列表中。

2、正确做法:

改成non-member函数:

constRationaloperator*(constRational&

lhs,constRational&

Rationalr(lhs.numerator()*rhs.numerator(),lhs.denominator()*rhs.denominator());

returnr;

此时2*r也可以正确运行

tips:

这个non-member函数不需要作为友元,因为他可以通过函数访问nu,de.

member函数的反面是non-member函数而不是friend函数。

NO25swap函数

有时swap函数效率很低(eg:

类A有一个指向向量的指针,而A的复制函数是需要将向量中的元素也复制。

此时swap函数会多次复制向量元素,而实际上只需要交互指针就行了)。

添加swap()成员函数,并提供class所在命名空间的swap非成员函数。

#include<

vector>

usingstd:

vector;

namespacemy{

template<

classT>

classA{

public:

A(vector<

T>

*_p):

voidswap(A&

vector<

*tmp=rhs.p;

rhs.p=p;

this->

p=tmp;

}

vector<

*p;

int*ptr;

};

voidswap(A<

&

lhs,A<

lhs.swap(rhs);

NO26尽可能延后变量定义式的出现时间

1、延迟变量定义到使用前一刻,并且尽量直接构造而不是复制。

2、

//方法1成本:

1构造+1析构+n赋值

MyClassA;

for(inti=0;

i<

n;

++i){

A=…

//方法2成本:

n构造+n析构

MyClassA=…

 

NO27尽量少做转型动作

1、static_cast的一个错误例子:

B():

i(0){}

virtualvoidpp(){++i;

inti;

classD:

publicB{

voidpp(){

B:

pp();

//static_cast<

B>

(*this).pp();

这样改变的是D的base部分的一个副本,所以最终i=1而不是2.

++i;

NO28避免返回指向对象内部成分的句柄

1、返回这样的句柄可能导致封装性下降

2、当句柄的寿命比所指对象长时会导致:

danglinghandles(因为对象被销毁时,句柄所指内容也不复存在,但是这个句柄被传到外界了,别人使用这个句柄时便指向了无效的内容。

NO29为异常安全而努力

1、异常安全的函数:

√不泄露资源(例如用Lock类代替lock()函数)

√不允许数据败坏

2、三个保证:

√基本承诺:

有效状态,但不知是哪种状态

√要么成功、要么完全失败。

√不抛异常

NO30inline函数

1、节省调用时间、增加代码大小(可能导致额外的换页)

2、只是一个申请,不是强制命令

3、virtual函数不可能是inline,因为执行前无法判断具体调用哪个函数,从而进行替换

4、函数指针进行的调用通常不会inline

inlinevoidfunc(){cout<

dosomething"

void(*fptr)()=func;

func();

//通常是inline的

fptr();

//通常不会是inline

5、看似简单的构造函数、析构函数有时也不适合inline,因为构造、析构函数中会调用成员变量、基类的构造、析构函数,这些加起来可能相当庞大。

NO31将文件的编译依存关系降至最低

1、引用和指针只需要类型声明,而对象需要定义式

classNode;

classTree{

Node&

root;

//正确

Node*root;

Noderoot;

//需要:

#include"

Node.h"

2、函数声明中用到对象,可以只提供声明

classPeople;

Peoplekiss(Peoplegf);

//如果加上{…}变成函数定义就不合法了

好处:

namespacemyLib{

classData;

classVector;

classString;

//...

Datafunc1();

voidfunc2(Vectorv);

Stringappend(Stringstr);

这样使用func1()的用户只需要includeData.h,使用func2()的用户只需要includeVector.h……这样就是的用户不需要使用一个函数而引入所有其他函数需要的类。

3、使用句柄类和接口类能解除接口和实现之间的耦合关系、减少头文件的编译依赖关系(同时他们也能避免用户知道类的实现细节,增加安全性)。

NO32public继承是is-a关系

1、baseclass中重载基类方法会将基类中该方法,和该方法的所有重载方法不可见。

classB{

virtualvoidf1()=0;

virtualvoidf1(double){cout<

B:

f1(double)"

voidf2(){cout<

f2()"

voidf2(int){cout<

f2(int)"

publicB{

usingB:

f1;

f2;

virtualvoidf1(){cout<

D:

f1()"

Dd;

d.f1();

d.f1(1.1);

//需要加入语句usingB:

d.f2();

d.f2

(1);

//需要加入语句usingB:

2、对于这种遮蔽的情况加入using声明可以解决,但是用using会把改名字的所有方法引进来。

而private继承中有时只需要用到其中某些函数:

例如,Derive类可能只需要Base类中无参数的函数f2、有参数的f1,此时需要用到转交函数:

classD2:

privateB{

voidf1(doubled){

f1(d);

voidf2(){

f2();

此时D2的f1(double)、f2()可用而f1()、f2(int)不可用。

NO33区分接口继承和实现继承

1、纯虚函数purevirtual只提供接口

2、普通虚函数提供接口和默认实现

3、非虚函数提供接口和强制实现

PS:

普通虚函数的默认实现可能带来的问题:

由于疏忽,子类使用了默认实现,但是这个实现不符合子类的业务逻辑。

解决方法:

1、提供一个接口和一个protected实现函数。

但是会带来命名污染。

2、方法声明提供接口、方法定义提供默认实现:

classAirplane{

virtualvoidfly()=0;

voidAirplane:

fly(){cout<

默认实现"

//A型飞机适用于默认实现

classModelA:

publicAirplane{

voidfly(){

Airplane:

fly();

//B型飞机不适用与默认实现

classModelB:

//此时B必须要提供fly的实现,因此也就不会由于疏忽而错误

cout<

B的实现"

NO34用非virtual函数代替virtual函数的手法

1、NVI非虚函数接口调用虚函数实现

public:

voidspeak(){

doSpeak();

}

virtualvoiddoSpeak(){cou

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 法律资料

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2