<Effective C++>读书摘要--Designs and Declarations<一>
<Item 18> Make interfaces easy to use correctly and hard to use incorrectly
1、That being the case, if they use one incorrectly, your interface is at least partially to blame. Ideally, if an attempted use of an interface won't do what the client expects, the code won't compile; and if the code does compile, it will do what the client wants.Developing interfaces that are easy to use correctly and hard to use incorrectly requires that you consider the kinds of mistakes that clients might make.
2、Many client errors can be prevented by the introduction of new types. Indeed, the type system is your primary ally in preventing undesirable code from compiling.如下可以防止类型参数使用错误
struct Day { struct Month { struct Year {
explicit Day(int d) explicit Month(int m) explicit Year(int y)
:val(d) {} :val(m) {} :val(y){}
int val; int val; int val;
}; }; };
class Date {
public:
Date(const Month& m, const Day& d, const Year& y);
...
};
Date d(, , ); // error! wrong types
Date d(Day(), Month(), Year()); // error! wrong types
Date d(Month(), Day(), Year()); // okay, types are correct
进一步可以如下编码防止月份范围使用错误。不使用enum,因为enum类是整数,不能防止类型范围使用错误,使用静态函数不使用常量,是保证变量有效的初始化。
class Month {
public:
static Month Jan() { return Month(); } // functions returning all valid
static Month Feb() { return Month(); } // Month values; see below for
... // why these are functions, not
static Month Dec() { return Month(); } // objects
... // other member functions
private:
explicit Month(int m); // prevent creation of new
// Month values
... // month-specific data
};
Date d(Month::Mar(), Day(), Year());
3、Another way to prevent likely client errors is to restrict what can be done with a type. A common way to impose restrictions is to add const. For example, Item 3 explains how const-qualifying the return type from operator* can prevent clients from making this error for user-defined types:
if (a * b = c) ... // oops, meant to do a comparison!
4、一个一般化的指导方针:unless there's a good reason not to, have your types behave consistently with the built-in types. Clients already know how types like int behave, so you should strive to have your types behave the same way whenever reasonable. For example, assignment to a*b isn't legal if a and b are ints, so unless there's a good reason to diverge from this behavior, it should be illegal for your types, too. When in doubt, do as the ints do.The interfaces to STL containers are largely (though not perfectly) consistent, and this helps make them fairly easy to use. For example, every STL container has a member function named size that tells how many objects are in the container. Contrast this with Java, where you use the length property for arrays, the length method for Strings, and the size method for Lists; and with .NET, where Arrays have a property named Length, while ArrayLists have a property named Count.
5、Any interface that requires that clients remember to do something is prone to incorrect use, because clients can forget to do it.直接返回std::tr1::shared_ptr<Investment> 可以避免资源忘记释放,同时可以在createInvestment中指定使用的deleter函数,当需要使用特定的函数释放资源的时候防止用户忘记指定特定的delete函数。
Investment* createInvestment(); // from Item 13; parameters omitted
// for simplicity
std::tr1::shared_ptr<Investment> createInvestment(); //这种方法更好一些
6、创建空的std::tr1::shared_ptr<Investment> 方法如下
std::tr1::shared_ptr<Investment> // attempt to create a null
pInv(, getRidOfInvestment); // shared_ptr with a custom deleter;
// this won't compile std::tr1::shared_ptr<Investment> // create a null shared_ptr with
pInv(static_cast<Investment*>(), // getRidOfInvestment as its
getRidOfInvestment); // deleter; see Item 27 for info on
// static_cast
std::tr1::shared_ptr<Investment> createInvestment()
{
std::tr1::shared_ptr<Investment> retVal(static_cast<Investment*>(),
getRidOfInvestment); retVal = ... ; // make retVal point to the
// correct object
return retVal;
}
Of course, if the raw pointer to be managed by pInv could be determined prior to creating pInv, it would be better to pass the raw pointer to pInv's constructor instead of initializing pInv to null and then making an assignment to it. For details on why, consult Item 26.
7、An especially nice feature of tr1::shared_ptr is that it automatically uses its per-pointer deleter to eliminate another potential client error, the "cross-DLL problem." This problem crops up when an object is created using new in one dynamically linked library (DLL) but is deleted in a different DLL. On many platforms, such cross-DLL new/delete pairs lead to runtime errors. tr1::shared_ptr avoids the problem, because its default deleter uses delete from the same DLL where the tr1::shared_ptr is created.
8、The most common implementation of tr1::shared_ptr comes from Boost (see Item 55). Boost's shared_ptr is twice the size of a raw pointer, uses dynamically allocated memory for bookkeeping and deleter-specific data, uses a virtual function call when invoking its deleter, and incurs thread synchronization overhead when modifying the reference count in an application it believes is multithreaded. (You can disable multithreading support by defining a preprocessor symbol.) In short, it's bigger than a raw pointer, slower than a raw pointer, and uses auxiliary dynamic memory. In many applications, these additional runtime costs will be unnoticeable, but the reduction in client errors will be apparent to everyone.
9、Things to Remember
Good interfaces are easy to use correctly and hard to use incorrectly. Your should strive for these characteristics in all your interfaces.
Ways to facilitate correct use include consistency in interfaces and behavioral compatibility with built-in types.
Ways to prevent errors include creating new types, restricting operations on types, constraining object values, and eliminating client resource management responsibilities.
TR1::shared_ptr supports custom deleters. This prevents the cross-DLL problem, can be used to automatically unlock mutexes (see Item 14), etc.
<Item 19>Treat class design as type design
10、You should therefore approach class design with the same care that language designers lavish on the design of the language's built-in types.Designing good classes is challenging because designing good types is challenging. Good types have a natural syntax, intuitive semantics, and one or more efficient implementations.
11、How, then, do you design effective classes? First, you must understand the issues you face. Virtually every class requires that you confront the following questions, the answers to which often lead to constraints on your design:
How should objects of your new type be created and destroyed? How this is done influences the design of your class's constructors and destructor, as well as its memory allocation and deallocation functions (operator new, operator new[], operator delete, and operator delete[] — see Chapter 8), if you write them.
How should object initialization differ from object assignment? The answer to this question determines the behavior of and the differences between your constructors and your assignment operators. It's important not to confuse initialization with assignment, because they correspond to different function calls (see Item 4).
What does it mean for objects of your new type to be passed by value? Remember, the copy constructor defines how pass-by-value is implemented for a type.
What are the restrictions on legal values for your new type? Usually, only some combinations of values for a class's data members are valid. Those combinations determine the invariants your class will have to maintain. The invariants determine the error checking you'll have to do inside your member functions, especially your constructors, assignment operators, and "setter" functions. It may also affect the exceptions your functions throw and, on the off chance you use them, your functions' exception specifications.
Does your new type fit into an inheritance graph? If you inherit from existing classes, you are constrained by the design of those classes, particularly by whether their functions are virtual or non-virtual (see Items 34 and 36). If you wish to allow other classes to inherit from your class, that affects whether the functions you declare are virtual, especially your destructor (see Item 7).
What kind of type conversions are allowed for your new type? Your type exists in a sea of other types, so should there be conversions between your type and other types? If you wish to allow objects of type T1 to be implicitly converted into objects of type T2, you will want to write either a type conversion function in class T1 (e.g., operator T2) or a non-explicit constructor in class T2 that can be called with a single argument. If you wish to allow explicit conversions only, you'll want to write functions to perform the conversions, but you'll need to avoid making them type conversion operators or non-explicit constructors that can be called with one argument. (For an example of both implicit and explicit conversion functions, see Item 15.)
What operators and functions make sense for the new type? The answer to this question determines which functions you'll declare for your class. Some functions will be member functions, but some will not (see Items 23, 24, and 46).
What standard functions should be disallowed? Those are the ones you'll need to declare private (see Item 6).
Who should have access to the members of your new type? This question helps you determine which members are public, which are protected, and which are private. It also helps you determine which classes and/or functions should be friends, as well as whether it makes sense to nest one class inside another.
What is the "undeclared interface" of your new type? What kind of guarantees does it offer with respect to performance, exception safety (see Item 29), and resource usage (e.g., locks and dynamic memory)? The guarantees you offer in these areas will impose constraints on your class implementation.
- How general is your new type? Perhaps you're not really defining a new type. Perhaps you're defining a whole family of types. If so, you don't want to define a new class, you want to define a new class template.
Is a new type really what you need? If you're defining a new derived class only so you can add functionality to an existing class, perhaps you'd better achieve your goals by simply defining one or more non-member functions or templates.
These questions are difficult to answer, so defining effective classes can be challenging. Done well, however, user-defined classes in C++ yield types that are at least as good as the built-in types, and that makes all the effort worthwhile.
12、Things to Remember
Class design is type design. Before defining a new type, be sure to consider all the issues discussed in this Item.
<Effective C++>读书摘要--Designs and Declarations<一>的更多相关文章
- <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++>读书摘要--Designs and Declarations<三>
<Item 22> Declare data members private 1.使数据成员private,保持了语法的一致性,client不会为访问一个数据成员是否需要使用括号进行函数调 ...
- <Effective C++>读书摘要--Implementations<二>
<Item29> Strive for exception-safe code. 1.如下面的代码 class PrettyMenu { public: ... void changeBa ...
- <Effective C++>读书摘要--Inheritance and Object-Oriented Design<二>
<Item 36> Never redefine an inherited non-virtual function 1.如下代码通过不同指针调用同一个对象的同一个函数会产生不同的行为Th ...
- <Effective C++>读书摘要--Resource Management<二>
<Item 15> Provide access to raw resources in resource-managing classes 1.You need a way to con ...
- <Effective C++>读书摘要--Ctors、Dtors and Assignment Operators<二>
<Item 9> Never call virtual functions during construction or destruction 1.you shouldn't call ...
- <Effective C++>读书摘要--Templates and Generic Programming<一>
1.The initial motivation for C++ templates was straightforward: to make it possible to create type-s ...
- <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++>读书摘要--Implementations<一>
1.For the most part, coming up with appropriate definitions for your classes (and class templates) a ...
随机推荐
- ASP.NET Core学习网站推荐
跟大家推荐一个不错的学习.NET Core 的网站,这个网站的视频是付费的,但是录视频的都是.NET Core的大佬们,个人觉得很不错推荐出来 video.jessetalk.cn
- mongodb C++ Driver安装
前言 mongocxx官网地址 http://mongocxx.org/?jmp=docs 本文的安装版本是:mongocxx-r3.2.0.tar.gz . 参考文档安装过程http://mongo ...
- CentOS6的/etc/rc.local不执行的问题解决
小编在一个场景下,希望java -jar abc.jar命令在系统开机自启动是自动执行,于是乎在文件/etc/rc.local中写入nohup java -jar abc.jar &,重启后发 ...
- 壹度DIY_微信小程序组件_小程序插件开发
开源免费插件,diy特有的页面机制,搭配30+自定义组件,让你的站点每一个页面都可以完全自定义,可无缝对接任意小程序,如有疑问加入qq壹度小程序交流群:302866773:或wx:liu2417301 ...
- header()函数用处
header() 函数向客户端发送原始的 HTTP 报头. 认识到一点很重要,即必须在任何实际的输出被发送之前调用 header() 函数(在 PHP 4 以及更高的版本中,您可以使用输出缓存来解决此 ...
- Linux的基础命令大全
linux的基础命令大全 1.shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口(命令解释器) ls -al /bin/sh ls -al /bin/bash 查看这些shell的 ...
- POJ-2155:Matrix(二维树状数祖)
Matrix Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 31892 Accepted: 11594 Descript ...
- fedora/centos下gcc编译出现gcc: error trying to exec ‘cc1plus’: execvp: No such file or directory
fedora/centos下gcc编译出现gcc: error trying to exec 'cc1plus': execvp: No such file or directory解决办法 翻译自: ...
- 24-集成ASP.NETCore Identity采用EF
1-增加IdentityServer4.AspNetIdentity nuget包 2- StartUp.cs启用增加相应的代码 .AddAspNetIdentity<ApplicationU ...
- 每天看一片代码系列(二):WebSocket-Node
简介 我们都知道,websocket主要是通过在浏览器和服务端建立长连接,继而实现二者的相互数据通信.不同于HTTP的轮询,它不会有大量无效的HTTP消息交换,从而节省了花销.websocket其实就 ...