Bjarne Stroustrup's FAQ: C ++ style and skills

zhaozj2021-02-16  65

(Translation: The translation of this article is quite hard. Bjarne Stroustrup is worth opening a generation of C language, not only thinking deeply, but also in the words of the words, there are many places, the translator repeatedly considers, can not achieve the ideal effect Only do you have to do our best. TextML format documentation See the Character: http://www.wushuang.net If you have any comments and suggestions for this translation, please send a letter to the translation: onekey@163.com. The address of the original text is: http://www.research.att.com/~bs/bs_faq2.html) (Dr. Bjarne Stroustrup) (Born in Denmark in 1950, graduated from the University of Arus, United Kingdom, AT & T Large-scale program design research department, AT & T Bell Lab and ACM Member. In 1979, b. S began to develop a language. At that time, it was called "c with class", and later evolved as C . 1998, ANSI / ISO C The standard is established, the same year, b. S The third edition of the C Programming Language is launched.) This is some people who often ask questions about C style and techniques. If you can make a better problem, or ask for these answers, please send me email (BS@research.att.com). Keep in mind that I can't spend all the time to update my homepage. See my General FAQ for more questions. For terms and concepts, see My C Glossary (C Glossary.). Please note that this is just a list of common problems and answers. It cannot replace a well-selected example and explanation in an excellent textbook. It also provides detailed and accurate descriptions like a reference manual or language standard. For questions about C , see "The Design and Evolution Of C ). For the use of the C language and the standard library, see "C Programming Language" (THE C Programming Language).

Directory: How do I write this very simple program? Why do you have to spend so long? Why is an empty class not of 0? Do I have to give data on the class declaration? Why is the member function not to virtual? Why is the destructor not Virtual by default? Why can't you have a virtual constructor? Why is overloaded in inheritance? Can I call a virtual function in the constructor? Is there a "Delete" (Placement DELETE)? Can I prevent someone from inheriting my own class? Why can't I define constrays for template parameters? Since there is already an excellent QSort () function, why still need a sort ()? What is a function object (Function Object)? How should I deal with memory leak? Why can't I continue after capturing an exception? Why is there a function equivalent to a realloc () in C ? How to use an exception? How to read a string from the input? Why does C does not provide "Finally" construct? What is an automatic pointer (Auto_PTR), why don't you automatically array (Auto_Array)? Can I mix use C-style and C style memory distribution and redistribution? Why do I have to use a shape to convert * void? How do I write this very simple program? Especially in the beginning of a semester, I often receive many asks about writing a very simple program. The most typical solution of this problem is to read it repeatedly, do something, and write the answer. Here is an example of this: #include #include #include using namespace std; int main () {vector v; double d; while (CIN >> D) V. PUSH_BACK (D); // Read Element IF (! cin.eof ()) {// Check the input whether it is wrong CERR << "format error / n"; return 1; // Return an error} cout << "Read "<< v.size () <<" Elements / N "; Reverse (v.begin (), v.end ()); cout <<" Elements in reverse Order: / n "; for (int i = 0 i

Remember to add the source file with the .cpp suffix, otherwise the compiler may think that it is a C code rather than C . Yes, the main () function returns an INT value. To read a standard vector (Vector), you can avoid overflow errors in the buffer of the size. Read in an array, do not produce "simple error", which has exceeded a novice capability - if you did it, then you are not a novice. If you expressed this, I suggest you read my article "Learn Standard C as a new language" ("Learning Standard C As a New Language), you can list your book (My Publications List) Download it in it. Cin.eof () is a check in convection format. In fact, it checks whether the loop is ended in finding an end-of-file (if not, then means that the input is not in the given format). For more description, see Section of the "Stream State" in your C textbook. Vector knows its own size, so I don't need to calculate the number of elements. This program does not contain explicit memory management. Vector maintains a stack in a memory to store its elements. When a vector requires more memory, it assigns some; it will release memory when it no longer survives. Thus, the user does not need to care about the memory allocation and release of elements in Vector. The program ends when encountering an "end-file". If you run it under the UNIX platform, "end-file" is equal to Ctrl D on the keyboard. If you are in the Windows platform, you may tend to use this slightly more complex version by using a bug, you can use the word "end" to indicate that the input has ended.

#include #include #include #include using namespace std; int main () {Vector v; double d; while (cin >> d) v.push_back (d) ); // read an element if (! Cin.eof ()) {// check whether the input is failed cin.clear (); // Clear the error stating String S; cin >> s; // Find the end character IF ( s! = "end") {cerr << "format error / n"; return 1; // Return error}} cout << "read" <

Look at this typical object-oriented program example: Class Shape {public: // Interface to users of shapes virtual void draw () const; virtual void rotate (int de devid); // ... protected: // Common data For import center; color color; {}; // ... protected: int RADIUS; // ... protected: int RADIUS; / / ... protected: int RADIUS; / / ... PROTECTED: INT RADIUS; // / ...}; Class Triangle: PUBLIC Shape {public: void draw () const; void rotate (int); // ... protected: Point A, B, C; // ...}; design idea is The user manipulates them through the PUBLIC interface of Shape, while the implementation of the derived class (such as a Circle and Triangle) shares the part of the PROTECTED member. This is not an easy thing: determine which implementation part is useful for all derived classes and shares it. Therefore, Protected members often do much more changes than the public interface. For example, although the theoretical "center" is a valid concept for all graphics, it is a very troublesome thing when you want to maintain a triangular "center" - for triangles, When it is only when it is really needed, it makes sense. Protected members are likely to rely on the details of the implementation, while Shape's users (translation: User is translated into users, referring to the code, the same as the shape class, the same), but not necessarily rely on them. For example, many (most?) Use Shape's code to logically unrelated to "color", but due to the existence of "color" in Shape, it may require a bunch of complex header files to combine operations The color concept of the system. When the Protected section changes, the code to use Shape must be recompiled - even if only the implementation of the derived class can access the protected member. Thus, "INFORTION Helpful to Implement" in the base class becomes as sensitive to the user, and its existence leads to unstable and unstable compilation of user code. (When the implementation is changed), and the header file is not controlled in the user code (because "" implementation related information "needs them). Sometimes this is called "BRITTLE BASE CLASS Problem). A very obvious solution is to ignore "implementation-related information" as those in the base class. In other words, use the interface, pure interface.

That is to say, use the abstract base class to indicate the interface: Class Shape {public: // Interface to users of shapes virtual void Draw () const = 0; Virtual Void Rotate (int devot) = 0; Virtual Point Center () Const = 0; // ... // NO DATA}; Class Circle: Public Shape {public: Void Draw () const; void Draw (int) {} point center () const {return center;} // .. COLOR COL; INT RADIUS; // ...}; class triangle: public shape {public: void draw () const; void rotate (int); point center () const; // ... Protected: Color Color Color Color COL; Point A, B, C; // ...}; Now, the relationship between the change in the implementation part of the user code and the derivative class is isolated. I have seen this technology to reduce the time of compilation. However, what if there is indeed public information useful for all derived classes (or only some derived classes)? You can simply package these information into a class, then derived a class from it: Class Shape {public: // interface to users of shapes virtual void draw () const = 0; Virtual Void Rotate (int De De De De De De De De De De De De De De De De De De De De De De De De De De Virtual point center () const = 0; // ... // no data}; struct common {color color color color: // ...}; Class Circle: Public Shape, protected common {public: void draw () const; Void Rotate (int) {} point center () const {return center;} // ... protected: point; int RADIUS;}; class triangle: public shape, protected common {public: void draw () const; void Rotate (int); point center () const; // ... protected: Point A, B, C;}; Why is an empty class size not 0? To be clear, the address of the two different objects is also different. Based on the same reason, NEW always returns a pointer to different objects.

Take a look: Class Empty {}; void f () {EMPTY A, B; if (& a == & b) cout << "Impossible: Report Error to Compiler Suppirl"; EMPTY * P1 = New Empty; EMPTY * P2 = New Empty; if (p1 == p2) cout << "Impossible: Report Error to Compiler Suppilier";} There is an interesting rule: an empty base class does not necessarily have a separate byte. Struct x: Empty {Int a; // ...}; void f (x * p) {void * p1 = p; void * p2 = & p-> a; if (p1 == p2) cout << "Nice : Good Optimizer ";} This optimization is allowed, which can be widely used. It allows programmers to use empty classes to express some simple concepts. Some compilers are now available for this "empty base class optimization). Do I have to give data on the class declaration? no need. If an interface does not require data, it is not necessary to give data in a class as an interface definition. In order to give them in the derived class. See "Why do you have to spend so long?". Sometimes you must give data in a class. Consider the case of Class Complex: Template class complex {public: complex (): re (0), IM (0) {} complex (Scalar R): RE (R), IM (0 ) {} Complex (Scalar R, Scalar I): RE (R), IM (i) {} // ... complex & operator = (const complex & a) {re = a.re; im = a.im; return * this;} // ... private: scal RE, IM;}; Design This type of purpose is to use it as a built-in type. The value is required to be assigned to ensure that the true local object (for, for example, those that are in the stack instead of being assigned in the stack), or make some simple operations appropriate inline . For those languages ​​that support built-in composite types, it is necessary to obtain the efficiency of them, and the real local objects and inline are necessary. Why is the member function not to virtual? Because many classes are not designed as base classes. For example, composite classes. Moreover, an object containing a virtual function is to take up more space to implement a virtual function call mechanism - it is often a word that occupies a word (Word). This additional word is very considerable, and may cause trouble (eg C or Fortran languages) when compatibility involving data relating to other languages. For more design principles, see "The Design and Evolution Of C ). Why is the destructor not Virtual by default? Because many classes are not designed as base classes.

Only class is in behavior is its derived interface (these derived classes are often distributed in the heap, accessible through pointers or quotes), and virtual functions make sense. So when you should define the destructor as a virtual? When the class has at least one virtual function. Having virtual functions means a class is a derived interface, in which case a derived class may be destroyed by a base class pointer. For example: class base {// ... virtual ~ base ();}; class deive: public base {// ... ~ derived ();}; void f () {base * p = new derived; delete P ; // Virtual destructor guaranteed ~ Derived function is called} If the analyte function of the base class is not a virtual, the sect of the sect of the class will not be called - this may produce a bad result, such as derived class Resources will not be released. Why can't you have a virtual constructor? Virtual call is a mechanism that works in the case where a given information is incomplete (given partial information). In particular, virtual allows us to call a function, for this function, only know its interface, and do not know the specific object type. But to create an object, you have to have full information. In particular, you need to know the specific type of object to be established. Therefore, the call to the constructor cannot be virtual. When an object is required, an indirect technology is often used as a "virtual constructor". For example, see the "C Programming Language" third edition 15.6.2. The following example shows a mechanism: how to use an abstract class to create an appropriate type of object. Struct f {// Object set function interface Virtual a * make_an_a () const = 0; Virtual B * Make_a_b () const = 0;}; Void User (const f & fac) {a * p = fac.make_an_a (); // As a suitable type B * q = fac.make_a_b (); // uses B as the appropriate type // ...} struct fx: f {a * make_an_a () const {return new AX (); } // AX is a derived b * Make_a_b () const {return new bx ();} // ax is a derived}; struct fy: f {a * make_an_a () const {return new aY ();} // AY is a derived b * Make_a_b () const {return new by ();} // BY is a derived}; int main () {user (fx ()); // This user establishes AX and BX User (fy ()); // This user establishes AY and BY / / ...} This is a deformerus of the "factory mode". The key is that the user of the user function is completely separated from the information of the class of AX or AY.

Why is overloaded in inheritance? This problem (very common) often appears in such an example: #include using namespace std; class b {public: int F (int 1) {cout << "f (int):"; return i 1 ;} // ...}; Class D: public b {public: double f (double d) {cout << "f (double):"; Return D 1.3;} // ...}; int main () {D * pd = new d; cout << pd-> f (2) << '/ n'; cout << pd-> f (2.3) << '/ n';} It is the result of its output is : F (Double): 3.3 f (Double): 3.6 is not like some people guess: f (int): 3 f (double): 3.6 In other words, there is no overloading between B and D Analysis. The compiler is looking for D's area and finds a function Double F (Double) and executes it. It never involves the area (encapsulated) B. In C , there is no overloading of the area - for this rule, inheritance is no exception. For more details, see "Design and Evolution of C Language" and "C Programming Language". However, if I need to establish a set of overloaded F () functions between base classes and inheritance classes? Very simple, use using declaration: Class D: public b {public: using b :: f; // make every f from b Available Double F (Double D) {cout << "f (double):"; Return D 1.3;} // ...}; After this modification, the output will be: f (int): 3 f (double): 3.6, between the F () and D D of B, heavy The load is indeed realized, and one of the most suitable F () is selected. Can I call a virtual function in the constructor? Yes, but be careful. It may not work as you expect. In the constructor, the virtual call mechanism does not work because the overload of the inheritance has not occurred. Objects are created from the base class, "base classes are first inherited."

Take a look at this: #include #include using namespace std; class b {public: b (const string & ss) {cout << "b constructor / n"; f (ss);} Virtual void f Const string &) {cout << "b :: f / n";}}; Class D: public b {public: d: d (ss) {cout << "D Constructor / N"; } Void f (const string & ss) {cout << "D :: f / n"; s = ss;} private: string s;}; int main () {D D ("Hello");} After compilation Will output: b Constructor b :: f D constructor notice not d :: f. Imagine if it is for different rules, B :: b () can call D :: f (), what kind of consequences do: because constructor D :: d () has not yet run, D :: F () Will try to give it a parameter to the string S that has not yet initialized. The result is likely to cause immediate crash. The destructor runs under the mechanism of "inherited prior to base class", so the behavior and constructor of the virtual mechanism are the same: only local definitions is used - the virtual function is not called to avoid touching the object ( Now that it has been destroyed) part of the inheritance class. For more details, see "Design and Evolution of C Language" 13.2.4.2 and "C Programming Language" 15.4.3. Some people suggest that this is just a man-made rule that is realized. not like this. In fact, it is necessary to implement this unsafe method, which is not easy: call the virtual function directly in the constructor, just like calling other functions. However, this means that any virtual functions cannot be written because they need to rely on the fixed creation of the base class (INVARIANTS ESTABLISHED BASE CLASSES). This will result in a mess. Is there a "Delete" (Placement DELETE)? No, but if you need it, you can write one yourself. Look at this specified location creation (Placement New) put the object into a series of Arena; class arena {public: void * allocate (size_t); void deallocate (void *); // ...}; void * Operator new (SIZE_T SZ, ARENA & A);} ARENA A1 (Some Arguments); Arena A2 (Some Arguments); after this is implemented, we can write: x * p1 = new A1) X; Y * p2 = new (a1) y; z * p3 = new (a2) z; // ... However, how does these objects are destroyed correctly? There is no built-in "Placement Delete" corresponding to this "Placement New". The reason is that there is no universal method to ensure that it is used correctly.

In the C type system, there is nothing to make us confirmed that P1 must point to an object that is dispatched by the ARENA type A1. P1 may point to any place to anything dispatched. However, sometimes the programmer is known, so this is a way: Template Void Destroy (T * P, Arena & a) {if (p) {P-> ~ T (); // Explicit Destructor Call a.deallocate (p);}} Now we can write: Destroy (P1, A1); Destroy (P2, A2); Destroy (P3, A3); if Arena maintains the clue of the object it saved, you Even you can write a destructor you to avoid errors. This is also possible: Define a pair of mutually matching operators new () and delete () to maintain the type inheritance system in "C programming language" 15.6. See "C language design and evolution" 10.4 and "C programming language" 19.4.5. Can I prevent someone from inheriting my own class? Yes, but why do you want to do so? This is two common answers: efficiency: Avoid my function is virtual call security: guarantee that my class is not used as a base class (for example, to copy objects without worrying) according to my experience, efficiency reasons It is often unnecessary worried. In C , the virtual function call is so fast so that when they are actually used in a class containing the virtual function, it is not worth considering the runtime expenses worth considering. Note that the virtual call mechanism is only used when passing through a pointer or reference. When a function is called directly through the object name, the expenditure called by the virtual function can be easily optimized. If there is really true need, you have to close a class to prevent virtual calls, so you may first ask why they are virtual. I have seen some examples, and those who have poor performance performance are set to virtual, without other reasons, just because "we habits so doing". Another part of this problem, how to prevent classes from being inherited due to logical reasons, with a solution. Unfortunately, this program is not perfect. It is based on such a fact that most of the inheritance classes must establish a virtual base class. This is an example: class Usable; class Usable_lock {friend class Usable; private: Usable_lock () {} Usable_lock (const Usable_lock &) {}}; class Usable: public virtual Usable_lock {// ... public: Usable (); Usable (char *); // ...}; usable a; class dd: public usable {}; DD DD; // error: dd :: dd () cannot be accessed // usable_lock :: usable_lock () is a private member (From "Design and Evolution of C Language" 11.4.3) Why can't I define constraints for template parameters? Yes, and the method is very simple and universal.

Take a look at this: Template Void Draw_all (C.Begin (), c.end (), mem_fun (& shape :: DRAW));} If the type error occurs, it may be quite Complex for_each () call. For example, if the element type of the container is int, we will get a fuzzy errors related to for_each () () (because it is not possible to call an INT value to call the Method for calling Shape :: DRAW). In order to capture this error in advance, I will write this: Template Void Draw_all (container & c) {shape * p = c.front (); // accept only containers of shape * s for_each (c.begin (), c Hend (), MEM_FUN (& Shape :: DRAW));} For most compilers, the initialization of the intermediate variable P will trigger an easy-to-understand error. This trick is common in many languages ​​and must do this in all standard creations. In the code of finished product, I maybe you can write: Template void Draw_all (container & c) {type_typename container :: value_type t; can_copy (); // accept containers of only shape * S for_each (c.begin (), c.end (), Mem_Fun (& Shape :: Draw));} This is very clear, I am building an assertion. Can_copy template can be defined: Template struct can_copy {static void constraints (t1 a, t2 b) {t2 c = a; b = a;} can_copy () {void (* p) (T1, T2) = constraints;}}; can_copy check whether T1 can be assigned to T2. CAN_COPY Check that the T is a shape * type, or a pointer to an object pointing to the class of the class of the Shape class, or is converted to a type of Shape * type by the user. Note that this definition is thinned to the minimum: a row name to check the constraint, and the type of type to be checked is listed by the specified constraint to check the constraint to check the trigger check (through constructor) Note this definition Quite reasonable properties: You can express a constraint without declaring or copying variables, so the writer of the constraint can not use how the variable is initialized, whether the object can be copied, destroyed, and the like. (Of course, the exception of the situation to check these attributes.) Use the current compiler, do not need to define and use constraints for constraints, do not need to use the macro constraint failure, the compiler will give an acceptable error message. , Including "constraints" (give the user a clue), constraint name, and detailed errors that cause constraint failures (for example, "unable to use double * initialize Shape *").

So, in the C language, is there anything similar to can_copy - or better -? In "C language design and evolution", there is a problem in achieving this universal constraint in C . Since then, there have been many ways to make the constraint class more easily, while still triggering good error messages. For example, I trust the way I use the function pointer used in Can_copy, it originates from Alex Stepanov and Jeremy Siek. I don't think that can_copy () is already standardized - it requires more use. Similarly, in the C community, a variety of different constraints are used; which constraint template is most effective in extensive use, and has not reached an agreement. However, this way is very common, which is more common than the mechanism for constraint inspections provided by the language. In any case, when we write a template, we have the most abundant expression of C .

转载请注明原文地址:https://www.9cbs.com/read-15755.html

New Post(0)