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

zhaozj2021-02-08  255

Bjarne Stroustrup's FAQ: C style and technique translation: left light

(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 the document that can only be tried to. HTML format See the Translator Home: 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 original address is: http://www.research.att.com/~bs/bs_faq2.html)

(Dr. Bjarne Stroustrup, born in Denmark in 1950, graduated from the University of Arusher, Arussee, the United Kingdom, the AT & T large scale program design research department, AT & T Bell Lab and ACM members. In 1979, b. S begins Developing a language, it is called "c with class", and later evolved as C . In 1998, the ANSI / ISO C standard was established, the same year, B. S launched its classics THE C Programming Language third edition.)

This is some people who often ask questions about C 's 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 if the input is wrong CERR << "format error / n"; return 1 ; // Returns an error}

Cout << "read" << v.size () << "Elements / N";

Reverse (v.begin (), v.end ()); cout << "Elements in reverse order: / n"; for (int i = 0; i

Return 0; // Successful return}

Observation on this program:

This is a standard ISO C program that uses a standard library (STANDARD LIBRARY). The standard library tool declares in the namespace STD, packaged in the header file without .h suffix.

If you want to compile it under Windows, you need to compile it into a "console application". Remember to add the source file plus 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 generate "simple error", which has exceeded a novice ability - if you have done, 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 if the loop is terminated to find an end-of-file (if not the case, then means that the input does not follow 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; when it no longer survives, it releases memory. 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); // Reads an element if (! cin.eof ()) {// Check the input failed cin.clear (); // Clear the error status String s; cin >> s; // Find the end character if (s! = "end") {cerr << "format error / n"; return 1; // Return Error}}}

Cout << "read" << v.size () << "Elements / N";

Reverse (v.begin (), v.end ()); cout << "Elements in reverse order: / n"; for (int i = 0; i

Return 0; // Successful return}

For more information on the use of standard libraries to simplify things, see "Roaming Standard Library" in C Programming Language ".

Why do you have to spend so long?

Your compiler may have problems. Maybe it is too old, maybe you have a mistake when you install it, maybe you use the computer already an antique. I can't help you on the problem like this.

However, this is also very likely: the program you want to compile is very bad, so that the compiler has to check the number of header files and tens of thousands of lines of lines. In theory, this is avoidable. If this is the design problem of the library you purchased, you don't work for it (except for a better library), but you can organize your own code to better, in order to ask the reconstruction of the code after modifying the code. Minimize. Such a design will be better and more maintainable because they show a better conceptual separation. Look at this typical object-oriented program example:

Class shape {public: // interface to users of shapes virtual void draw () const; virtual void rotate (int devot); // ... protected: // Common data (for mail) Point Center; color color // ...};

Class circle: public shape {public: void draw () const; void rotate (int) {} // ... protected: int RADIUS; // ...};

Class Triangle: Public Shape {public: void draw () const; void rotate (int); // ... protected: Point A, B, C; // ...};

Design idea is that users can manipulate them via Shape's public interface, while the implementation of derived classes (such as Circle and Triangle) shares the part of the Protected member performance (Implement).

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 using the Shape class, but the same) is not necessarily relying 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 using Shape must recompile - even if only the derived implementation is part of the implementation of 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 rotate () const {return center;} // ... protected: point center; color color color color color color color color ..

Class Triangle: Public Shape {public: Void Draw () const; void rotate (int); point center () const; // ... protected: color color color color color color

The relationship between the user code and the change of the derived 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)? These information can be packaged simply, then derived from it to the class:

Class Shape {public: // interface to users of shapes virtual void draw () const = 0; Virtual void rotate (int De De degRees) = 0; Virtual point center () const = 0; // ...

// no data};

Struct Common {color color color color color: public shape, protected common; public: void draw () const; void rotate (int) {} point center () const {return center;} //. .. protected; →

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 not of 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 Suppilier";

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 Document bytes. 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 providing 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: scalar RE, IM;};

Designing 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 when class is in behavior is its derived interface (these derived classes are often allocated in the heap, the virtual function is meaningful to access through pointers or references).

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. E.g:

Class base {// ... virtual ~ base ();}; class deive: public base {// ... ~ derived ();

Void f () {base * p = new derived; delete p; // virtual destructor guarantee ~ Derived function is called}

If the designer function of the base class is not a virtual, the sect of the sects of the class will not be called - this may produce bad results, such as the resource of the party, 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 establish 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 (); // uses A as a suitable type B * q = fac.make_a_b (); // as a suitable 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 b 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 deformed "The Factory Pattern). The key is that the user of the user function is completely separated from the information such as 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):"; returni 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';}

The result of its output is:

f (Double): 3.3 f (double): 3.6 is not like some people who guess:

f (int): 3 f (double): 3.6

In other words, there is no resolution of overload between B and D. 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 statement:

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

Thus, between F () and D D () of B, the overload is indeed implemented, 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 (based on the Base Before Derive)."

have 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 (Const String & SS): B (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 the program is compiled:

B Constructor b :: f D Constructor

Be care 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 very easy to implement this unsafe method: 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 (Invariant Established by 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) {return a.allocate (SZ);

Arena A1 (Some Arguments); ARENA A2 (Some Arguments);

After this is achieved, we can write this:

X * p1 = new (a1) x; y * p2 = new (a1) y; z * p3 = new (a2) z; // ...

But how do these objects correctly 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 this:

Destroy (P1, A1); Destroy (P2, A2); Destroy (P3, A3);

If Arena maintains the clue of the object it saved, you can even write a destructor 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 anshes:

Efficiency: Avoid my function is virtual call security: Make sure my class is not used as a base class (for example, to ensure that I can copy objects without worrying)

According to my experience, the reason is often unnecessary. 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 constrays for template parameters?

Yes, and the method is very simple and universal.

have a look at this:

Template void Draw_all (container & c) {for_each (c.begin (), c.end (), MEM_FUN (& Shape :: DRAW);}

If a type error occurs, it may be when a fairly 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 wrote this:

Template Void Draw_all (container & c) {shape * p = c.front (); // accept Only Containers of Shape * S

For_each (C. Segin (), C.End (), 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 may write this:

Template void Draw_all (container & c) {typedef type {type_type t; can_copy (); // accept containers of only friend shape * sfor_each (c.begin (), c.end ), MEM_FUN (& Shape :: DRAW);

This is very clear, I am building an assertion. The CAN_COPY template can be defined like this:

Template struct can_copy {static void constraints (t1 a, t2 b) {t2 c = a; b = a;} can_copy () {void (* p) (t1, t2) = constructs;} }

CAN_COPY checks whether T1 can be assigned to T2 if it can be assigned. CAN_COPY Check that the T is a shape * type, or a pointer to the object of the class that is inherited by the shape class, or is converted by the user to a type of Shape * type. Note that this definition is thinned to the smallest:

One-line name to check the constraint, and the type of type to be checked, list the specified constraint to check the constraint to check the trigger check (through constructor)

Note this definition is quite reasonable:

You can express a constraint without a declaration or replication variable, so the writer of the constraint can not be used to envision 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 easier to write, 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 proved to be most effective in extensive use, and has not reached a consistent opinion.

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 . have a look at this:

Template struct derived_from {static void constraints (t * p) {b * pb = p;} Derived_From () {void (* p) (t *) = constraints;}}; Template struct can_copy {static void constraints (t1 a, t2 b) {t2 c = a; b = a;} can_copy () {void (* p) (t1, t2) = consTRAINTS;}};

Template struct can_compare {static void constraints (t1 a, t2 b) {a == b; A! = b; a

Template STRUCT CAN_MULTIPLY {Static Void Constraints (T1 A, T2 B, T3 C) {C = A * B;} CAN_MULTIPLY () {Void (* P) ​​(T1, T2 , T3) = construints;}};

Struct B {}; struct d: b {}; struct dd: d {}; struct x {};

INT main () {derived_from (); derived_from (); Derived_From (); derived_from (); derived_from ();

CAN_Compare (); can_compare (); can_multiply (); can_multiply (); can_multiply (); can_copy (); can_copy (); can_copy ();

// Typical "element must inherit from mybase *" constraint:

Template Class Container: Derived_From {// ...};

In fact, Derived_FROM does not check the source (DeriVation), but only checks the conversion, but this is often a better constraint. It is difficult to constrain a good name for the constraint.

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

New Post(0)