<Effective C++>读书摘要--Implementations<一>
1、For the most part, coming up with appropriate definitions for your classes (and class templates) and appropriate declarations for your functions (and function templates) is the lion's share of the battle. Once you've got those right, the corresponding implementations are largely straightforward. Still, there are things to watch out for. Defining variables too soon can cause a drag on performance. Overuse of casts can lead to code that's slow, hard to maintain, and infected with subtle bugs. Returning handles to an object's internals can defeat encapsulation and leave clients with dangling handles. Failure to consider the impact of exceptions can lead to leaked resources and corrupted data structures. Overzealous inlining can cause code bloat. Excessive coupling can result in unacceptably long build times.
<Item 26> Postpone variable definitions as long as possible
2、推迟变量的声明在C++中比在C中重要的多。在C语言中声明一个变量开销仅仅是堆栈上面的空间(通常只是在递归函数中才仔细考虑其性能影响),而在C++中则意味着构造函数和析构函数潜在的运行开销
3、Not only should you postpone a variable's definition until right before you have to use the variable, you should also try to postpone the definition until you have initialization arguments for it. By doing so, you avoid constructing and destructing unneeded objects, and you avoid unnecessary default constructions. Further, you help document the purpose of variables by initializing them in contexts in which their meaning is clear. 包含两方面 一个是推迟变量的定义 一个是在定义变量的时候直接初始化,避免调用了默认构造函数在赋值
// finally, the best way to define and initialize encrypted
std::string encryptPassword(const std::string& password)
{
if (password.length() < MinimumPasswordLength) {
throw logic_error("Password is too short"); // check length
} std::string encrypted(password); // define and initialize
// via copy constructor
encrypt(encrypted);
return encrypted;
}
4、对于循环中的变量,有下面两种方案
// Approach A: define outside loop // Approach B: define inside loop
Widget w;
for (int i = ; i < n; ++i){ for (int i = ; i < n; ++i) {
w = some value dependent on i; Widget w(some value dependent on i);
... ...
} }
- Approach A: 1 constructor + 1 destructor + n assignments.
Approach B: n constructors + n destructors.
但是A对代码的可理解性和可维护性产生了负面影响,As a result, unless you know that (1) assignment is less expensive than a constructor-destructor pair 即A更高效 and (2) you're dealing with a performance-sensitive part of your code,性能敏感, 两个条件满足时使用A。you should default to using Approach B.
5、Things to Remember
Postpone variable definitions as long as possible. It increases program clarity and improves program efficiency.
<Item 27> Minimize casting
6、The rules of C++ are designed to guarantee that type errors are impossible. In theory, if your program compiles cleanly, it's not trying to perform any unsafe or nonsensical operations on any objects. This is a valuable guarantee. You don't want to forgo it lightly.
7、Unfortunately, casts subvert the type system. That can lead to all kinds of trouble, some easy to recognize, some extraordinarily subtle. If you're coming to C++ from C, Java, or C#, take note, because casting in those languages is more necessary and less dangerous than in C++. But C++ is not C. It's not Java. It's not C#. In this language, casting is a feature you want to approach with great respect.
8、Let's begin with a review of casting syntax, because there are usually three different ways to write the same cast. C-style casts look like this:
(T) expression // cast expression to be of type T
Function-style casts use this syntax:
T(expression) // cast expression to be of type T
There is no difference in meaning between these forms; it's purely a matter of where you put the parentheses. I call these two forms old-style casts.
9、C++ also offers four new cast forms (often called new-style or C++-style casts):
const_cast<T>(expression) dynamic_cast<T>(expression) reinterpret_cast<T>(expression) static_cast<T>(expression)
Each serves a distinct purpose:
const_cast is typically used to cast away the constness of objects. It is the only C++-style cast that can do this.
dynamic_cast is primarily used to perform "safe downcasting," i.e., to determine whether an object is of a particular type in an inheritance hierarchy. It is the only cast that cannot be performed using the old-style syntax. It is also the only cast that may have a significant runtime cost. (I'll provide details on this a bit later.)
reinterpret_cast is intended for low-level casts that yield implementation-dependent (i.e., unportable) results, e.g., casting a pointer to an int. Such casts should be rare outside low-level code. I use it only once in this book, and that's only when discussing how you might write a debugging allocator for raw memory (see Item 50).
static_cast can be used to force implicit conversions (e.g., non-const object to const object (as in Item 3), int to double, etc.). It can also be used to perform the reverse of many such conversions (e.g., void* pointers to typed pointers, pointer-to-base to pointer-to-derived), though it cannot cast from const to non-const objects. (Only const_cast can do that.)
10、需要显示进行类型转换的往往是代码设计的薄弱点,但是很难完全避免。const_cast使用时需要谨慎,const在c++中重要程度远高于c,c++提供了mutable用于解决看起来需要const_cast解决的问题
11、The old-style casts continue to be legal, but the new forms are preferable. First, they're much easier to identify in code (both for humans and for tools like grep), thus simplifying the process of finding places in the code where the type system is being subverted. Second, the more narrowly specified purpose of each cast makes it possible for compilers to diagnose usage errors. For example, if you try to cast away constness using a new-style cast other than const_cast, your code won't compile.
12、About the only time I use an old-style cast is when I want to call an explicit constructor to pass an object to a function. For example:
class Widget {
public:
explicit Widget(int size);
...
}; void doSomeWork(const Widget& w); doSomeWork(Widget()); // create Widget from int
// with function-style cast
doSomeWork(static_cast<Widget>()); // create Widget from int
// with C++-style cast
Somehow, deliberate object creation doesn't "feel" like a cast, so I'd probably use the function-style cast instead of the static_cast in this case. Then again, code that leads to a core dump usually feels pretty reasonable when you write it, so perhaps you'd best ignore feelings and use new-style casts all the time.
13、Many programmers believe that casts do nothing but tell compilers to treat one type as another, but this is mistaken. Type conversions of any kind (either explicit via casts or implicit by compilers) often lead to code that is executed at runtime. For example, in this code fragment,
int x, y;
...
double d = static_cast<double>(x)/y; // divide x by y, but use
// floating point division
the cast of the int x to a double almost certainly generates code, because on most architectures, the underlying representation for an int is different from that for a double. That's perhaps not so surprising, but this example may widen your eyes a bit:
class Base { ... };
class Derived: public Base { ... };
Derived d;
Base *pb = &d; // implicitly convert Derived* Base*
Here we're just creating a base class pointer to a derived class object, but sometimes, the two pointer values will not be the same. When that's the case, an offset is applied at runtime to the Derived* pointer to get the correct Base* pointer value.This last example demonstrates that a single object (e.g., an object of type Derived) might have more than one address (e.g., its address when pointed to by a Base* pointer and its address when pointed to by a Derived* pointer). That can't happen in C. It can't happen in Java. It can't happen in C#. It does happen in C++. In fact, when multiple inheritance is in use, it happens virtually all the time, but it can happen under single inheritance, (虚函数表相关)。too. Among other things, that means you should generally avoid making assumptions about how things are laid out in C++, and you should certainly not perform casts based on such assumptions. For example, casting object addresses to char* pointers and then using pointer arithmetic on them almost always yields undefined behavior.
14、But note that I said that an offset is "sometimes" required. The way objects are laid out and the way their addresses are calculated varies from compiler to compiler. That means that just because your "I know how things are laid out" casts work on one platform doesn't mean they'll work on others. The world is filled with woeful programmers who've learned this lesson the hard way.
15、典型的类型转换导致的错误如下,红色部分实际在*this对象基类的拷贝上调用的onResize,紫色部分才是在*this对象基类上调用的。This example also demonstrates that if you find yourself wanting to cast, it's a sign that you could be approaching things the wrong way. This is especially the case if your want is for dynamic_cast.
class Window { // base class
public:
virtual void onResize() { ... } // base onResize impl
...
}; class SpecialWindow: public Window { // derived class
public:
virtual void onResize() { // derived onResize impl;
static_cast<Window>(*this).onResize(); // cast *this to Window,
// then call its onResize;
// this doesn't work!
Window::onResize(); // call Window::onResize// on *this ... // do SpecialWindow-
} // specific stuff
...
};
16、Before delving into the design implications of dynamic_cast, it's worth observing that many implementations of dynamic_cast can be quite slow. For example, at least one common implementation is based in part on string comparisons of class names. If you're performing a dynamic_cast on an object in a single-inheritance hierarchy four levels deep, each dynamic_cast under such an implementation could cost you up to four calls to strcmp to compare class names. A deeper hierarchy or one using multiple inheritance would be more expensive. There are reasons that some implementations work this way (they have to do with support for dynamic linking). Nonetheless, in addition to being leery of casts in general, you should be especially leery of dynamic_casts in performance-sensitive code.The need for dynamic_cast generally arises because you want to perform derived class operations on what you believe to be a derived class object, but you have only a pointer- or reference-to-base through which to manipulate the object. There are two general ways to avoid this problem.Neither of these approaches — using type-safe containers (直接保存派生类的指针避免cast) or moving virtual functions up the hierarchy(在基类中声明派生类函数的虚函数,直接使用基类指针调用避免cast) — is universally applicable, but in many cases, they provide a viable alternative to dynamic_casting. When they do, you should embrace them.
17、Like most suspicious constructs, casts should be isolated as much as possible, typically hidden inside functions whose interfaces shield callers from the grubby work being done inside.
18、Things to Remember
Avoid casts whenever practical, especially dynamic_casts in performance-sensitive code. If a design requires casting, try to develop a cast-free alternative.
When casting is necessary, try to hide it inside a function. Clients can then call the function instead of putting casts in their own code.
Prefer C++-style casts to old-style casts. They are easier to see, and they are more specific about what they do.
<Item 28> Avoid returning "handles" to object internals.
19、This immediately leads to two lessons. First, a data member is only as encapsulated as the most accessible function returning a reference to it(数据成员的封装性等于最宽松的访问函数的封装性). In this case, though ulhc and lrhc are declared private, they're effectively public, because the public functions upperLeft and lowerRight return references to them. Second, if a const member function returns a reference to data associated with an object that is stored outside the object itself, the caller of the function can modify that data, (This is just a fallout of the limitations of bitwise constness — see Item 3.)
20、Everything we've done has involved member functions returning references, but if they returned pointers or iterators, the same problems would exist for the same reasons. References, pointers, and iterators are all handles (ways to get at other objects), and returning a handle to an object's internals always runs the risk of compromising an object's encapsulation. As we've seen, it can also lead to const member functions that allow an object's state to be modified.
21、We generally think of an object's "internals" as its data members, but member functions not accessible to the general public (i.e., that are protected or private) are part of an object's internals, too. As such, it's important not to return handles to them. This means you should never have a member function return a pointer to a less accessible member function. If you do, the effective access level will be that of the more accessible function, because clients will be able to get a pointer to the less accessible function, then call that function through the pointer.
22、dangling handles(悬空引用): handles that refer to parts of objects that don't exist any longer ,如下面代码&(boundingBox(*pgo).upperLeft());语句执行完后(boundingBox(*pgo)产生的临时对象就会被销毁,因此pUpperLeft 指向了一个销毁的内部数据。
class GUIObject { ... };
const Rectangle // returns a rectangle by
boundingBox(const GUIObject& obj); // value; see Item 3 for why
// return type is const
GUIObject *pgo; // make pgo point to
... // some GUIObject
const Point *pUpperLeft = // get a ptr to the upper
&(boundingBox(*pgo).upperLeft()); // left point of its
// bounding box
23、This doesn't mean that you should never have a member function that returns a handle. Sometimes you have to. For example, operator[] allows you to pluck individual elements out of strings and vectors, and these operator[]s work by returning references to the data in the containers (see Item 3) — data that is destroyed when the containers themselves are. Still, such functions are the exception, not the rule.
24、Things to Remember
Avoid returning handles (references, pointers, or iterators) to object internals. It increases encapsulation, helps const member functions act const, and minimizes the creation of dangling handles.
<Effective C++>读书摘要--Implementations<一>的更多相关文章
- <Effective C++>读书摘要--Implementations<二>
<Item29> Strive for exception-safe code. 1.如下面的代码 class PrettyMenu { public: ... void changeBa ...
- <Effective C++>读书摘要--Designs and Declarations<一>
<Item 18> Make interfaces easy to use correctly and hard to use incorrectly 1.That being the c ...
- <Effective C++>读书摘要--Accustoming Youself to C++
<Item 1>View C++ as a federation of languages. 1.把C++看成4种子语言组成,即C.Object-Oriented C++.Template ...
- <Effective C++>读书摘要--Introduction
Introduction 1.Learning the fundamentals of a programming language is one thing; learning how to des ...
- <Effective C++>读书摘要--Inheritance and Object-Oriented Design<二>
<Item 36> Never redefine an inherited non-virtual function 1.如下代码通过不同指针调用同一个对象的同一个函数会产生不同的行为Th ...
- <Effective C++>读书摘要--Inheritance and Object-Oriented Design<一>
1.Furthermore, I explain what the different features in C++ really mean — what you are really expres ...
- <Effective C++>读书摘要--Designs and Declarations<三>
<Item 22> Declare data members private 1.使数据成员private,保持了语法的一致性,client不会为访问一个数据成员是否需要使用括号进行函数调 ...
- <Effective C++>读书摘要--Designs and Declarations<二>
<Item 20> Prefer pass-by-reference-to-const to pass-by-value 1.By default, C++ passes objects ...
- <Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<一>
<Item 5> Know what functions C++ silently writes and calls 1.If you don't declare them yoursel ...
随机推荐
- PHP 使用GD库合成带二维码的海报步骤以及源码实现
PHP 使用GD库合成带二维码的海报步骤以及源码实现 在做微信项目开发过程中,经常会遇到图片合成的问题,比如将用户的二维码合成到宣传海报中,那么,遇到这种情况,利用PHP的GD库也是很容易实现的,实现 ...
- Vue 从零开始--搭建环境
简要:继项目空闲后,开始着手vue的学习;为此向大家分享其中的艰辛和搭建办法,希望能够跟各位VUE大神学习探索,如果有不对或者好的建议告知下:*~*! 一.什么是VUE? 是一种node.js框架,特 ...
- 【转载】SOCKS代理:从***到内网漫游
原文:SOCKS代理:从***到内网漫游 本文原创作者:tahf,本文属FreeBuf原创奖励计划,未经许可禁止转载 之前在Freebuf上学习过很多大牛写的关于Tunnel.SOCKS代理.***等 ...
- 图片文件转换成Base64编码实现ajax提交图片
//上传头像图片 function uploadHead(imgPath) { console.log("imgPath = " + imgPath); var image = n ...
- android学习十一 高级调试分析功能
1.debug 功能列表 2.ddms功能( 内存检查,线程检查,视图层次分析) 3.跟踪代码 TraceView 4.命令行工具 adb 5.策略检查StrictMode
- PLSQL集合类型
PLSQL集合类型 --联合数组(索引表) /* 用于存储某个数据类型的数据集合类型 .通过索引获得联合数组中得值 如下例子: */ DECLARE CURSOR cur_chars IS SEL ...
- 怎样下载JDBC驱动
MySQL官网: https://www.mysql.com/ 请注意: 需要把mysql-connector-java-5.1.45-bin.jar放到C:\JMeter\apache-jmeter ...
- MySql优化浅析
优化点:合理的使用索引,可以大幅度提升sql查询效率,特别查询的表的数据量大的时候,效果明显.一.引言 公司的产品XX出行上线正式运营,随着数据量的变大,司机2000+,日订单1万+,注册乘客26W+ ...
- ubuntu16.04图形界面安装中文输入法,中文展示
打开system Settings 设置 打开设置语言 安装Language Support 点击installed languages 选择chinese 打勾,安装 安装IBus框 ...
- 机器学习之支持向量机(Support Vector Machine)
转载请注明出处:http://www.cnblogs.com/Peyton-Li/ 支持向量机 支持向量机(support vector machines,SVMs)是一种二类分类模型.它的基本模型是 ...