📄 e.htm
字号:
if (value) { // if value ptr isn't null data = new char[strlen(value) + 1]; strcpy(data,value); } else { // handle null value ptr3 data = new char[1]; *data = '\0'; // add trailingnull char }}// a possible String assignment operatorString& String::operator=(const String& rhs){ if (this == &rhs) return *this; // see Item 17 delete [] data; // delete old memory data = // allocate new memory new char[strlen(rhs.data) + 1]; strcpy(data, rhs.data); return *this; // see Item 15}Notice how the constructor must check its parameter for validity and how it must take pains to ensure that the member data is properly initialized, i.e., points to a char* that is properly null-terminated. On the other hand, the assignment operator takes it for granted that its parameter is legitimate. Instead, it concentrates on detecting pathological conditions, such as assignment to itself (see Item 17), and on deallocating old memory before allocating new memory. The differences between these two functions typify the differences between object initialization and object assignment. By the way, if the "[]" notation in the use of delete is new to you (pardon the pun), Items 5 and M8 should dispel any confusion you may have.A final term that warrants discussion is client. A client is a programmer, one who uses the code you write. When I talk about clients in this book, I am referring to people looking at your code, trying to figure out what it does; to people reading your class definitions, attempting to determine whether they want to inherit from your classes; to people examining your design decisions, hoping to glean insights into their rationale.You may not be used to thinking about your clients, but I'll spend a good deal of time trying to convince you to make their lives as easy as you can. After all, you are a client of the software other people develop. Wouldn't you want those people to make things easy for you? Besides, someday you may find yourself in the uncomfortable position of having to use your own code, in which case your client will be you!I use two constructs in this book that may not be familiar to you. Both are relatively recent additions to C++. The first is the bool type, which has as its values the keywords true and false. This is the type now returned by the built-in relational operators (e.g., <, >, ==, etc.) and tested in the condition part of if, for, while, and do statements. If your compilers haven't implemented bool, an easy way to approximate it is to use a typedef for bool and constant objects for true and false: typedef int bool;const bool false = 0;const bool true = 1;This is compatible with the traditional semantics of C and C++. The behavior of programs using this approximation won't change when they're ported to bool-supporting compilers. For a different way of approximating bool including a discussion of the advantages and disadvantages of each approach turn to the Introduction of More Effective C++.The second new construct is really four constructs, the casting forms static_cast, const_cast, dynamic_cast, and reinterpret_cast. Conventional C-style casts look like this: (type) expression // cast expression to be of // type typeThe new casts look like this: static_cast<type>(expression) // cast expression to be of // type typeconst_cast<type>(expression)dynamic_cast<type>(expression) <type>reinterpret_cast<type>(expression) <type>These different casting forms serve different purposes: const_cast is designed to cast away the constness of objects and pointers, a topic I examine in Item 21. dynamic_cast is used to perform "safe downcasting," a subject we'll explore in Item 39. reinterpret_cast is engineered for casts that yield implementation-dependent results, e.g., casting between function pointer types. (You're not likely to need reinterpret_cast very often. I don't use it at all in this book.) static_cast is sort of the catch-all cast. It's what you use when none of the other casts is appropriate. It's the closest in meaning to the conventional C-style casts.Conventional casts continue to be legal, but the new casting forms are preferable. They're much easier to identify in code (both for humans and for tools like grep), and the more narrowly specified purpose of each casting form makes it possible for compilers to diagnose usage errors. For example, only const_cast can be used to cast away the constness of something. If you try to cast away an object's or a pointer's constness using one of the other new casts, your cast expression won't compile.For more information on the new casts, see Item M2 or consult a recent introductory textbook on C++.In the code examples in this book, I have tried to select meaningful names for objects, classes, functions, etc. Many books, when choosing identifiers, embrace the time-honored adage that brevity is the soul of wit, but I'm not as interested in being witty as I am in being clear. I have therefore striven to break the tradition of using cryptic identifiers in books on programming languages. Nonetheless, I have at times succumbed to the temptation to use two of my favorite parameter names, and their meanings may not be immediately apparent, especially if you've never done time on a compiler-writing chain gang.The names are lhs and rhs, and they stand for "left-hand side" and "right-hand side," respectively. I use them as parameter names for functions implementing binary operators, especially operator== and arithmetic operators like operator*. For example, if a and b are objects representing rational numbers, and if rational numbers can be multiplied via a non-member operator* function, the expression a * bis equivalent to the function call operator*(a, b)As you will discover in Item 23, I declare operator* like this: const Rational operator*(const Rational& lhs, const Rational& rhs);As you can see, the left-hand operand, a, is known as lhs inside the function, and the right-hand operand is known as rhs.I've also chosen to abbreviate names for pointers according to this rule: a pointer to an object of type T is often called pt, "pointer to T." Here are some examples: string *ps; // ps = ptr to stringclass Airplane;Airplane *pa; // pa = ptr to Airplaneclass BankAccount;BankAccount *pba; // pba = ptr to BankAccountI use a similar convention for references. That is, rs might be a reference-to-string and ra a reference-to-Airplane.I occasionally use the name mf when I'm talking about member functions.On the off chance there might be some confusion, any time I mention the C programming language in this book, I mean the ISO/ANSI-sanctified version of C, not the older, less strongly-typed, "classic" C. Back to IntroductionContinue to Item 1: Prefer const and inline to #define.Shifting from C to C++Getting used to C++ takes a little while for everyone, but for grizzled C programmers, the process can be especially unnerving. Because C is effectively a subset of C++, all the old C tricks continue to work, but many of them are no longer appropriate. To C++ programmers, for example, a pointer to a pointer looks a little funny. Why, we wonder, wasn't a reference to a pointer used instead?C is a fairly simple language. All it really offers is macros, pointers, structs, arrays, and functions. No matter what the problem is, the solution will always boil down to macros, pointers, structs, arrays, and functions. Not so in C++. The macros, pointers, structs, arrays and functions are still there, of course, but so are private and protected members, function overloading, default parameters, constructors and destructors, user-defined operators, inline functions, references, friends, templates, exceptions, namespaces, and more. The design space is much richer in C++ than it is in C: there are just a lot more options to consider.When faced with such a variety of choices, many C programmers hunker down and hold tight to what they're used to. For the most part, that's no great sin, but some C habits run contrary to the spirit of C++. Those are the ones that have simply got to go. Back to Shifting from C to C++Continue to Item 2: Prefer <iostream> to <stdio.h>Item 1: Prefer const and inline to #define.This Item might better be called "prefer the compiler to the preprocessor," because #define is often treated as if it's not part of the language per se. That's one of its problems. When you do something like this, #define ASPECT_RATIO 1.653the symbolic name ASPECT_RATIO may never be seen by compilers; it may be removed by the preprocessor before the source code ever gets to a compiler. As a result, the name ASPECT_RATIO may not get entered into the symbol table. This can be confusing if you get an error during compilation involving the use of the constant, because the error message may refer to 1.653, not ASPECT_RATIO. If ASPECT_RATIO was defined in a header file you didn't write, you'd then have no idea where that 1.653 came from, and you'd probably waste time tracking it down. This problem can also crop up in a symbolic debugger, because, again, the name you're programming with may not be in the symbol table.The solution to this sorry scenario is simple and succinct. Instead of using a preprocessor macro, define a constant: const double ASPECT_RATIO = 1.653;This approach works like a charm. There are two special cases worth mentioning, however.First, things can get a bit tricky when defining constant pointers. Because constant definitions are typically put in header files (where many different source files will include them), it's important that the pointer be declared const, usually in addition to what the pointer points to. To define a constant char*-based string in a header file, for example, you have to write const twice: const char * const authorName = "Scott Meyers";For a discussion of the meanings and uses of const, especially in conjunction with pointers, see Item 21.Second, it's often convenient to define class-specific constants, and that calls for a slightly different tack. To limit the scope of a constant to a class, you must make it a member, and to ensure there's at most one copy of the constant, you must make it a static member: class GamePlayer {private:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -