Effective C++ 读书笔记7

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

请记住:宁可拿non-member non-friend函数替换member函数,这样做可以增加封装性,包裹弹性和机能扩展性


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

请记住:如果你需要为某个函数的所有参数(包括this指针所指的那个隐喻参数)进行类型转换,那么这个函数必须是个non-member


条款25:考虑写出一个不抛一场的swap函数


5 实现

1)太快定义变量可能造成效率上的拖延

2)过度使用转型可能导致代码变慢又难维护,又招来微妙难解的错误

3)返回对象内部数据之号码牌(handles)可能会破坏封装并留给客户虚吊号码牌(dangling handles);

4)未考虑一场带来的冲击则可能导致资源泄漏和数据败坏;

5)过度热心的inline可能引起代码膨胀;

6)过度耦合(coupling)则可能导致让人不满意的冗长build time


条款26:尽可能延后变量定义的出现时间

    只要你定义了一个变量而其类型带有一个构造函数和析构函数,那么当程序的控制流到达这个变量定义时,你便得承受构造成本;当这个变量离开作用域时,你便得承受析构成本。或许你会认为,你不可能定义一个不使用的变量,那么请看:

std::string encryptPassword(const std::string& password){
	
	using namespace std;
	string encrypted;
	if(password.length() < MinimumPasswordLength){
		
		throw logic_error("password is too shart");
	}
	
	return encrypted;
}

对象encrypted在此函数中并非完全未被使用,但如果有个异常被丢出,它就真的没被使用。所以应该这样:

std::string encryptPassword(const std::string& password){
	
	using namespace std;
	
	if(password.length() < MinimumPasswordLength){
		
		throw logic_error("password is too shart");
	}
	string encrypted;
	//...
	return encrypted;
}
但是这段代码仍然不够秾纤合度,因为encrypted虽然获得定义但却无任何实参作为初值,这意味着调用的是其默认构造函数,条款4解释了为什么通过默认构造函数构造出一个对象然后对它赋值比直接在构造时指定初值的效率差。

std::string encryptPassword(const std::string& password){
	
	//...
	std::string encrypted;
	encrypt(encrypted);
	return encrypted;
}

更受欢迎的做法是以password作为encrypted的初值,跳过毫无意义的默认构造函数:


std::string encryptPassword(const std::string& password){
	
	//...
	std::string encrypted(password);
	encrypt(encrypted);
	return encrypted;
}

这让我们联想起本条款所谓尽可能延后的真正意义。你不只应该延后变量的定义,直到非得使用该变量的前一刻为止,甚至应该尝试延后这份定义直到能够个他初值实参为止。如果这样,不仅能够避免构造和析构非必要对象,还可以避免无意义的默认构造行为。

关于循环:

。。。。。。


请记住:

尽可能延后变量定义式的出现。这样做可增加程序的清晰度并改善程序效率。


条款27: 尽量少做转型动作

cast破坏了类型系统,那可能导致任何种类的麻烦,有些容易辨识,有些非常隐晦。

C风格的cast动作看起来像这样:

(T)expression

函数风格的转型动作看起来像这样:

T(expression) (????????????)

两种形式并无差别,纯粹只是小括号的摆放位置不同而已。C++提供四种新式转型:

1.const_cast通常被用来将对象的常量性移除。

2.dynamic_cast主要用来执行安全向下转型,也就是用来决定某对象是否归属继承体系中的某个类型。它是唯一无法由旧式语法执行的动作,也是唯一可能消耗重大运行成本的转型动作。

3.reinterpret_cast意图执行低级转型,实际动作可能取决于编译器,这也就表示它不可移植。例如将一个pointer to int转型为一个int。这一类转型的低级代码以外很少见。本书只使用一次,那是在讨论如何针对原始内存写出一个调试用的分配器时,见条款50.

4.static_cast用来强迫隐士转换,例如将non-const对象转为const对象,或将int转为double等等。它也可以用来执行上述多种转换的反向转换,例如将void*转为typed指针,将pointer-to-base转为pinter-to-derived.但它无法将const转为non-const.


class Base{};
class Derived: public Base{};
Derived d;
Base* pb = &d;
这里我们不过是建立一个base class指针指向一个derived class对象,但有时候上述的两个指针值并不相同。这种情况下会有个偏移量在运行期间被施行于Derived*指针身上,用以取得正确的Base*指针值。

上述例子表明,单一对象可能拥有一个以上的地址(例如当以Base*指向它时的地址和以Derived*指向它时的地址)。??????????

另一个关于转型的有趣事情是:我们可能容易写出某些似是而非的代码,例如许多应用框架都要求derived classes内的virtual函数代码的第一个动作就先调用base class的对应函数。以下代码看起来对,但是是错误的:

class Window{
public:
	virtual void onResize(){}
	//...
};
class SpecialWindow: public Window{
public:
	virtual void onResize(){
		
		static_cast<Window>(*this).onResize();
		//...
	}
};

恐怕你没想到的是,它调用的并不是当前对象上的函数,而是稍早转型动作所建立的一个“*this对象之base class成分”的暂时副本身上的onResize(译注:函数就是函数,成员函数只有一份,调用起哪个对象身上的函数有什么关系呢?关键在于成员函数都有个隐藏的this指针,会因此影响函数操作的数据)(???????????)。所以如果Window::onResize修改了对象内容,当前对象其实没被改动,改动的只是副本。然而SpecialWindow::onResize内如果也修改对象,当前对象真的会被改动。这样的结果是:其base class成分的更改没有落实,而derived class成分的更改倒是落实了。(整段都没看懂)

所以应该这样写:

class SpecialWindow: public Window{
public:
	virtual void onResize(){
		
		Window::onResize();
		//...
	}
};

之所以需要dynamic_cast,通常是因为你想在一个你认定为derived class对象身上执行derived class操作函数,但你手上却只有一个指向base的pointer或reference。两个方法可以避免这个问题:

方法一:使用容器并在其中直接指向derived class对象的指针(通常是只能指针):

不应该这样做:

class Window{};
class SpecialWindow: public Window{
public:
	void blink();
	//...
};
typedef std::vector<std::shared_ptr<Window> > VPW;
VPW winPtrs;

for(VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter){
	
	if(SpecialWindow* psw = dynamic_cast<SpecialWindow*>(iter->get())){
		
		psw->blink();
	}
}

应该这样做:

typedef std::vector<std::shared_ptr<SpecialWindow> > VPSW;
VPSW winPtrs;
for(VPSW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter){
	
	(*iter)->blink();
}

方法二:该方法可以让你通过base class接口处理所有可能之各种Window派生类,那就是在base class内提供virtual函数做你想对各个Window派生类做的事。

class Window{
public:
	virtual void blink() {}  //do nothing
	//...
};
class SpecialWindow: public Window{
public:
	virtual void blink(){}; //do something
	
};

typedef std::vector<std::shared_ptr<Window> > VPW;
VPW winPtrs;
for(VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); iter++){
	
	(*iter)->blink();
}

请记住:

1.如果可以,尽量避免cast,特别是在注重效率的代码中避免dynamic_cast。如果有个设计需要cast,试着发展无需转型的替代设计。

2.如果转型是必须的,试着将它隐藏在某个函数背后。客户随后可以调用该函数,而不需要将转型放进他们自己的代码中。

3.宁可使用C++-style转型,不要使用旧式转型,前者容易识别出来,而且也比较有着分门别类的职掌。

内容简介: 有人说C++程序员可以分成两类,读过Effective C++的和没读过的。世界顶级C++大师Scott Meyers成名之作的第三版的确当得起这样的评价。当您读过《Effective C++中文版(第3版改善程序与设计的55个具体做法)》后,就获得了迅速提升自己C++功力的一个契机。   在国际上,本书所引起的反响,波及整个计算机技术出版领域,余音至今未绝。几乎在所有C++书籍的推荐名单上,本书都会位于前三名。作者高超的技术把握力、独特的视角﹑诙谐轻松的写作风格﹑独具匠心的内容组织,都受到极大的推崇和仿效。这种奇特的现象,只能解释为人们对这本书衷心的赞美和推崇。   《Effective C++中文版(第3版改善程序与设计的55个具体做法)》不是读完一遍就可以束之高阁的快餐读物,也不是用以解决手边问题的参考手册,而是需要您去反复阅读体会的,C++是真正程序员的语言,背后有着精深的思想与无与伦比的表达能力,这使得它具有类似宗教般的魅力。希望这本书能够帮助您跨越C++的重重险阻,领略高处才有的壮美风光,做一个成功而快乐的C++程序员。 Effective C++中文版(第3版改善程序与设计的55个具体做法)》一共组织55个准则,每一条准则描述一个编写出更好的C++的方式。每一个条款的背后都有具体范例支撑。第三版有一半以上的篇幅是崭新内容,包括讨论资源管理和模板(templates)运用的两个新章。为反映出现代设计考虑,对第二版论题做了广泛的修订,包括异常(exceptions)、设计模式(design patterns)和多线程(multithreading)。   《Effective C++中文版(第3版改善程序与设计的55个具体做法)》的重要特征包括:   ·高效的 classes、functions、templates 和inheritance hierarchies(继承体系)方面的专家级指导。   ·崭新的 "TR1" 标准程序库功能应用,以及与既有标准程序库组件的比较。   ·洞察 C++和其他语言(例如Java、C#、C)之间的不同。此举有助于那些来自其他语言阵营的开发人员消化吸收 C++ 式的各种解法。 目录: 译序 中英简繁术语对照 目录 序言 致谢 导读 1.让自己习惯C++ 条款01:视C++为一个语言联邦 条款02:尽量以const,enum,inline替换#define 条款03:尽可能使用const 条款04:确定对象被使用前已先被初始化 2.构造/析构/赋值运算 条款05:了解C++默默编写并调用哪些函数 条款06:若不想使用编译器自动成生的函数,就该明确拒绝 条款07:为多态基类声明Virtual析构函数 条款08:别让异常逃离析构函数 条款09:绝不在构造和析构过程中调用Virtual函数 条款10:令Operator=返回一个referenceto this 条款11:在Operator=中处理“自我赋值” 条款12:复制对象时勿忘其每一个成分 3.资源管理 条款13:以对象管理资源 条款14:在资源管理类中小心Coping行为 条款15:在资源管理类中提供对原始资源的访问 条款16:成对使用new和delete对象置入智能指针 条款17:以独立语句将newed对象置入智能指针 4.设计与声明 条款18:让接口容易被正确使用,不易被误用 条款19:设计class犹如设计type 条款20:宁以pass-by-reference-to-const替换Pass-by-value 条款21:必须返回对象时,别妄想返回其reference 条款22: 将成员变量声明为private 条款23: 宁以non-member、non-friend替换member函数 条款24:若有所参数皆需类型转换,请为此采用non-member函数 条款25:考虑写出一个不抛异常的swap函数 5.实现 条款26:尽可能延后变量定义式的出现时间 条款27:尽量少做转型动作 条款28:避免返回handles指向对象内部成分 条款29:为“异常安全”而努力是值得的 条款30:透彻了解inlining的里里外外 条款31:将文件间的编译依存关系降至最低 6.继承与面向对象设计 条款32:确定你的public继承塑模出is-a关系 条款33:避免遮掩继承而来的名称 条款34:区分接口继承和实现继承 条款35:考虚virtual函数以外的其他选择 条款36:绝不重新定义继承而来的non-virtual函数 条款37:绝不重新定义继承而来的缺省参数值 条款38:通过复合塑模出has-a或“根据某物实现出” 条款39:明智而审慎地使用private继承 条款40:明智而审慎地使用private继承 7.模板与泛型编程 8.定制new和delete 9.杂项讨论 A 本书之外 B 新旧版条款对映 索引
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值