📄 e.htm
字号:
class SmartPointer { // template definitionpublic: SmartPointer(T *p = 0); ~SmartPointer(); T * operator->() const; T& operator*() const; ...};That brings us to constructors. A default constructor is one that can be called without any arguments. Such a constructor either has no parameters or has a default value for every parameter. You generally need a default constructor if you want to define arrays of objects: class A {public: A(); // default constructor};A arrayA[10]; // 10 constructors calledclass B {public: B(int x = 0); // default constructor};B arrayB[10]; // 10 constructors called, // each with an arg of 0class C {public: C(int x); // not adefault constructor};C arrayC[10]; // error!You may find that your compilers reject arrays of objects when a class's default constructor has default parameter values. For example, some compilers refuse to accept the definition of arrayB above, even though it receives the blessing of the C++ standard. This is an example of the kind of discrepancy that can exist between the standard's description of C++ and a particular compiler's implementation of the language. Every compiler I know of has a few of these shortcomings. Until compiler vendors catch up to the standard, be prepared to be flexible, and take solace in the certainty that someday in the not-too-distant future, the C++ described in the standard will be the same as the language accepted by C++ compilers.Incidentally, if you want to create an array of objects for which there is no default constructor, the usual ploy is to define an array of pointers instead. Then you can initialize each pointer separately by using new: C *ptrArray[10]; // no constructors calledptrArray[0] = new C(22); // allocate and construct // 1 C objectptrArray[1] = new C(4); // ditto...This suffices almost all the time. When it doesn't, you'll probably have to fall back on the more advanced (and hence more obscure) "placement new" approach described in Item M4.Back on the terminology front, a copy constructor is used to initialize an object with a different object of the same type: class String {public: String(); // default constructor String(const String& rhs); // copy constructor ...private: char *data;};String s1; // call default constructorString s2(s1); // call copy constructorString s3 = s2; // call copy constructorProbably the most important use of the copy constructor is to define what it means to pass and return objects by value. As an example, consider the following (inefficient) way of writing a function to concatenate two String objects: const String operator+(String s1, String s2){ String temp; delete [] temp.data; temp.data = new char[strlen(s1.data) + strlen(s2.data) + 1]; strcpy(temp.data, s1.data); strcat(temp.data, s2.data); return temp;}String a("Hello");String b(" world");String c = a + b; // c = String("Hello world")This operator+ takes two String objects as parameters and returns one String object as a result. Both the parameters and the result will be passed by value, so there will be one copy constructor called to initialize s1 with a, one to initialize s2 with b, and one to initialize c with temp. In fact, there might even be some additional calls to the copy constructor if a compiler decides to generate intermediate temporary objects, which it is allowed to do (see Item M19). The important point here is that pass-by-value means "call the copy constructor."By the way, you wouldn't really implement operator+ for Strings like this. Returning a const String object is correct (see Items 21 and 23), but you would want to pass the two parameters by reference (see Item 22).Actually, you wouldn't write operator+ for Strings at all if you could help it, and you should be able to help it almost all the time. That's because the standard C++ library (see Item 49) contains a string type (cunningly named string), as well as an operator+ for string objects that does almost exactly what the operator+ above does. In this book, I use both String and string objects, but I use them in different ways. (Note that the former name is capitalized, the latter name is not.) If I need just a generic string and I don't care how it's implemented, I use the string type that is part of the standard C++ library. That's what you should do, too. Often, however, I want to make a point about how C++ behaves, and in those cases, I need to show some implementation code. That's when I use the (nonstandard) String class. As a programmer, you should use the standard string type whenever you need a string object; the days of developing your own string class as a C++ rite of passage are behind us. However, you still need to understand the issues that go into the development of classes like string. String is convenient for that purpose (and for that purpose only). As for raw char*-based strings, you shouldn't use those antique throw-backs unless you have a very good reason. Well-implemented string types can now be superior to char*s in virtually every way including efficiency (see Item 49 and Items M29-M30).The next two terms we need to grapple with are initialization and assignment. An object's initialization occurs when it is given a value for the very first time. For objects of classes or structs with constructors, initialization is always accomplished by calling a constructor. This is quite different from object assignment, which occurs when an object that is already initialized is given a new value: string s1; // initializationstring s2("Hello"); // initializationstring s3 = s2; // initializations1 = s3; // assignmentFrom a purely operational point of view, the difference between initialization and assignment is that the former is performed by a constructor while the latter is performed by operator=. In other words, the two processes correspond to different function calls.The reason for the distinction is that the two kinds of functions must worry about different things. Constructors usually have to check their arguments for validity, whereas most assignment operators can take it for granted that their argument is legitimate (because it has already been constructed). On the other hand, the target of an assignment, unlike an object undergoing construction, may already have resources allocated to it. These resources typically must be released before the new resources can be assigned. Frequently, one of these resources is memory. Before an assignment operator can allocate memory for a new value, it must first deallocate the memory that was allocated for the old value.Here is how a String constructor and assignment operator could be implemented: // a possible String constructorString::String(const char *value){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -