C ++ Style and Technique FAQ (Chinese)

zhaozj2021-02-16  50

C Style and Technique FAQ (Chinese)

Bjarne Stroustrup, Ziyun English Translation

[Note: Translate the translation of this interview with Dr. Stroustrup. To reprint, please contact me:

ZMelody@sohu.com

]

Q: This simple program ... How do I get it?

A: Some people often ask me some simple programs to write, this is especially at the beginning of the semester. A typical problem is: How to read some numbers, do some processing (such as mathematical operation), then output ... well, here I give a "general demonstration program":

#include

#include

#include

Using namespace std;

Int main () {vector

v; double d; while (cin >> d) v.push_back (d); // read elements if (! cin.eof ()) {// check if INPUT failed CERR << "format error / n"; return 1; // error return} Cout << "read" << v.size () << "Elements / N"; Reverse (v.begin (), v.end ()); cout << "Elements in Reverse Order: / n "; for (int i = 0; i

The program is very simple, yes. Here is some "observation reports" for it:

This is a program written by standard C , using standard libraries [translation: standard library mainly standardizes the original C library, the Iostream library, STL (Standard Template Library, Standard Template) and other standardization Resulting. The features provided by the standard library are located in NameSpace STD, and the header files you need to use the standard library are not .h extensions. [Translation: Some compiler vendors provide a header file containing .h extensions. ] If you compile under Windows, you need to set the compilation option to "console Application". Remember, your extension of your source code must be .cpp, otherwise the compiler may treat it as a C code. The main function main () is to return an integer. [Demolition: Some compilers also support the definition of void main (), but this is non-standard practice] The VECTOR container that will be entered into the standard library can guarantee that you will not commit "buffer overflow". "For beginners For example, it is a thing that "read the input into an array, do not make any 'stupid error'" seems to be a bit too much - if you can achieve this request, then you can't make a complete beginner. If you don't believe in my argument, then please see the article I wrote "Learning Standard C As a New Language". [Translation: The 9CBS document area has this article. ] Code "! Cin.eof ()" is used to test the format of the input stream. Specifically, it tests whether the cycle of the read input stream is terminated by EOF. If not, that means that the input format is not pair (not all numbers). There are also details where you can see the chapters of the "flow state" in the textbook you are using. Vector is aware of its own size, so you don't have to enter how much elements you have. This program does not include any explicit memory management code, nor does it produce memory leaks. Vector automatically configures memory, so users don't have to worry about this. For how to read a string, see "How do I read the String from the standard input" entry. This program takes EOF for the logo of the input termination. If you run this program on UNIX, you can enter EOF with Ctrl-D. But the Windows version you use may contain a bug (http://support.microsoft.com/support/kb/articles/q156/2/58.asp?ln=en-us&sd=gn&r=0&qry=end of file & rnk = 11 & src = DHCS_MSPSS_GN_SRCH & SPR = NTW40), causing the system to identify EOF characters. If so, then you may have a slightly changed program more suitable for you: this program "end" as an input end sign. # include # include

#include

#include

Using namespace std; int main () {vector

V; Double D; While (CIN >> D) v.push_back (d); // read elements if (! cin.eof ()) {// check if INPUT failed cin.clear (); // clear error state String S; CIN >> S; // Look for Terminator String IF (s! = "end") {cerr << "format error / n"; return 1; // error return}} cout << "read" <

Q: Why do I compile a program to spend so much time?

A: Perhaps your compiler is a bit unbelieved - it is not an old age, or is not properly installed? Maybe your computer will enter the museum ... I am really love for such a problem.

However, it is also possible because your program - can you improve your programming? Is the compiler not to eat the correct binary code and have to eat hundreds of head files, tens of thousands of source code? In principle, as long as the source code is appropriately optimized, the problem with slow compilation should be able to solve. If the crux is your library vendor, then you know that there is nothing to do in addition to "replacement of a library supplier"; but if the problem is your own code, you can completely let the refactoring. Your code is more structured, so that the source code needs to be recompiled once when there is change. Such code is often a better design: because it has a low degree of intensity, it is better maintenance. Let's take a classic example of OOP: Class Shape {public: // Interface to users of shapes virtual void Draw () const; virtual void rotate (int de devot); // ... protected: // circon data (for importers Point center; color color: public shape {public: void draw () const; void mark (int) {} // ... protected: int RADIUS; //. .}; Class Triangle: PUBLIC Shape {public: Void Draw () const; void rotate (int); // ... protected: Point A, B, C; // ...};

The design philosophy of the above code is: let users handle the "various shapes" through the common interface of Shape; while the SHPE protection member provides the functions needed by the inheritance class (such as Circle, Triangle). That is to say: the public factors of various shapes (Shapes) are went to the base class Shape. This concept seems to be very reasonable, but I want to draw your attention:

To confirm "which features will be used by all inheritance classes, but in the base class" can not be a simple matter. Therefore, the protection member of the base class may vary with the required changes, and its frequency is much higher than the possible variation of the common interface. For example, although we regard "center" as a attribute of all shapes (so as to declare in the base class) seems to be the sky, therefore, it is very troublesome to maintain the center coordinates of the triangle in the base class. It is necessary to calculate when it is needed - this reduces overhead. Unlike abstract common interfaces, protect members may rely on actual details, and this is the user of the Shape class unwilling to see. For example, most of the use of Shape should be logically unrelated to the Color; but as long as the Color's statement appears in the Shape class, it will often cause the compiler to define the header file read in the "The Color Representation in the operating system" , Expand, compile. This requires time! When the work of protecting members (such as previous center, color) changes, all the code that uses the Shape class needs to recompile - even if only these code is very small, it is really necessary to use the base class. The "semantic change of semantics". Therefore, in the base class, put some "The actual help of inheritance" may be good, but the fact is the source of trouble. The user's requirements are changeable, so the actual code is also changeable. Place the variable code in many inheritance classes, the change can not be partial, which will cause global impact! Specifically: a header file relying on by the base class changes, then all the documents in which all inheritance classs need to be recompiled. After that, after the analysis, the solution will be obvious: only the base class is used as an abstract common interface, and the "useful" on the inheritance "is removed. 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} Class circle: public shape {public: void draw () const; void rotate () const {return center;} // ... protected: point center; 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 color ...}; This, the change in inheritance is isolated. The recompilation time brought by the change can be extremely significantly shortened.

However, if there are some functions to be shared by all inheritance classes (or just several inheritance classes), do not want to repeat these codes in each inheritance class, what should I do? Also: package these features into a class, if inherited, you can use these features, let it inherit this class: Class Shape {public: // Interface to users of shapes virtual void draw () const = 0; Virtual Void Rotate (int De De De De De De) = 0; Virtual Point Center () const = 0; // ... // no data}; struct common {color color color color color color color: public shape, protected CommON {public: void Draw () const; void rotate () const {} point center;} // ... protected: point cent; int RADIUS;}; Class Triangle: Public Shape, Protected Common {public: void Draw () const; void rotate (int); point center () const; // ... protected: Point A, B, C;}; [Translation] Here's the author's ideas is isolated and reduced coupling . From this example, the reader can learn a little refactoring entry knowledge: o)]

Q: What kind of empty class is not zero?

A: To ensure that the address of the two different objects is different, it must be. Because of this, the pointer returned by NEW always points to different individual objects. We still come to see the code:

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";

In addition, there is a fun rule in C - empty groups do not need another byte to represent:

Struct x: Empty {Int a; // ...}; void f (x * p) {void * p1 = p; void * p2 = & p-> a; if (p1 == p2) cout << "Nice : Good Optimizer ";

If P1 and P2 are equal in the above code, then the compiler is optimized. Such optimization is safe and very useful. It allows programmers to express very simple concepts with empty classes without having to pay extra (space) costs for this. Some modern compilers provide this "virtual base optimization" function.

Q: Why do I have to put the data in a statement?

A: No one is forcing you to do this. If you don't want to have data in the interface, don't put it in the class defined interface, put it in the inheritance class. See "Why do I compile a program to spend so much time" entry. [Translation: This FAQ is a declaration for the translation of DECLARE / DECLAration; define / definition is translated into definitions. Basic differences between the two, see "'int * p;' and 'int * p;' to which of the correct" entry. In general, we will also refer to the following example code as the definition of the Complex class, and will sing a single line "Class Complex;" called a statement. ] However, sometimes you do need to put the data in the class statement, such as an example of the following 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; }

This Complex class is designed to be used as a C built-in type, so the data indicates that in the declaration, so that a real local object (ie, the object allocated on the stack, not allocating in the heap) This also ensures that simple operations can be connected correctly. "Local objects" and "inline" are important because this can make our complex class to reach the language of the built-in complex type.

[Translation: I think this answered "escape problem" is a bit "evading problem". I think, the true intention of the questioner may be to know how to use C to completely separated "on" " Unfortunately, the C language and class mechanism itself does not provide this way. We all know that the "interface" section of the class is often defined as public (generally some virtual functions); "Some" sections are often defined as protected or private (including functions and data); but whether it is "public" segment or "Protected", "private" segments must appear in the category declaration, provided with the header files where the class declaration is located. I want to come this is what "why the data must be placed in the class declaration" problem. In order to solve this problem, we have a variety of ways: Use the Proxy mode (see "Design Patterns: Elements of Reusable Object-Oriented Software", we can declare the actual part in the Proxy class (called "object combination "), Without exposing the declaration of the Proxy class to the user. E.g:

Class Implementer; // Forward Declaration Class Interface {public: // interface private: import device Impl;

In this example, the Implementer class is Proxy. It is only a "stub" of an IMPL object in Interface to expose it to the user. The Implementer class can declare the following:

Class Implementer {public: // Implementation Details, Including Data Members

}

The comments in the above code can store the "data" said by the questioner, and the implementation code of the Implementer does not need to be exposed to the user. However, Proxy model is not a perfect-all-all-all, an interpl pointer, has brought additional overhead. Perhaps the reader will say that C is not an inline mechanism? This overhead makes up by inline definition. But don't forget, the purpose of using the Proxy mode is to hide the "actual" section, this "hidden" often means "actual code" exists in the form of binary code in the link library. Does the current C compiler and linker can do both "code inline" and "binary hidden"? Maybe it may. So can the Proxy mode and the C template mechanism "happy"? (In other words, if the statement of Interface and Implementer in the previous code is not Class, but Template, what is it?) The key is whether the compiler needs to copy the source code to the inline and template's support. Carry the binary code copy. At present, the implementation of the generic support of C # is on the Intermediate Language level, and C is on the source level. The complex class declaration code given by Bjarne "" Data must appear in the class declaration "is also part of this consideration. Oh, it's far away ... After all, this text is just FAQ "translation", this is not more discussion, interested readers can find answers themselves: o)] Q: Why is the member function not default?

A: Because many classes are not used to be used. [Translation: The class used to do the base class is often similar to the Interface concept in other languages ​​- their role is to define a common interface for a set of classes. However, the classes in C clearly have many other uses - such as representing a specific extension type. ] For example, the complex number is like this.

In addition, there is a virtual function of a virtual function overhead [Translation] Refers to the space overhead brought by VTable and the time overhead brought by indirect calls in vTable. In general, the space overhead of each object is increased. Word head. This overhead is not small, and it will cause the incompatibility with other languages ​​(such as C, Fortran) - the memory data layout and ordinary classes of the class with virtual functions are very different. [Translation: This compatibility issue of this memory data layout will bring trouble to multi-language mixed programming. ]

There are more details on the design concept in "The Design and Evolution Of C ".

Q: What is the descendant function is not the default?

A: Ha, you probably know what I want to say: o) Still because - many classes are not used to make base classes. The virtual function is only meaningful when the class is used as an interface. (Such classes often instantiate objects on memory and access by pointers or quotes.)

So, when should I let the destructive function be imaginary? Oh, the answer is - when there is other virtual functions, you should make the destructive function to be empty. Have other virtual functions, it means that this class is to be inherited, which means it tastes a bit "interfab". In this way, the programmer may point to the object that is instantiated by its inheritance class with a base class, and whether it can be used by the base class pointer to the normal release of whether or not it is virtual. . E.g:

Class base {// ... virtual ~ base ();}; class deive: public base {// ... ~ derived ();}; void f () {base * p = new deerived; delete p; / / virtual destructor used to ensure That ~ derived is caled} If the destructor is not virtual, then Derive's destructor will not be called - this often brings evil: such as the resources allocated in Derived released.

Q: Why doesn't C do not have a virtual constructor?

A: The design purpose of the virtual mechanism is to allow the programmer to use the object without fully understanding the details (such as only the class implements an interface, without knowing what is exactly what is stuff). However, to establish an object, you can't just know "this is a matter of", "you must fully understand all details, clearly know what the object you want to build is what is. Therefore, the constructor cannot be inevitable.

However, sometimes there is also a certain amount of indirectness when establishing an object, which requires a point of skill. (See "The C Programming Language", third edition, 15.6.2), sometimes referred to as "virtual constructor". I will take an example of using an abstract class to "virtual constructors":

Struct f {// interface to object creation functions 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 (); // Make an A of the appropriate Type B * q = fac.make_a_b (); // Make a b of the appropriate type // ...} struct fx: f {a * make_an_a () const {return new AX );} // ax is deact from ab * make_a_b () const {return new bx ();} // bx is derived from b}; struct fy: f {a * make_an_a () const {return new aY (); } // is deived from ab * make_a_b () const {return new by ();} // by is derived from b}; int main () {user (fx ()); // this user makes axs and bxs User (fy ()); // this user makes ays and bys // ...}

I understand? The above code actually uses a variant of the Factory mode. The key is that user () is completely isolated - it doesn't know anything to AX, AY. (嘿, sometimes ignorance, ignorant benefits ^ _ ^)

Q: Why can't you overload it in derived class?

A: This problem is often produced in this example:

#include

Using namespace std; class b {public: int F (int) {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 the program is:

f (double): 3.3 f (double): 3.6

Instead of some people (incorrectly) guess:

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

In other words, there is no overload between D and B. You call PD-> f (), the compiler is looking for ah in the name domain of D. If you find Double F (Double), call it. The compiler is too lazy to go to B's name domain to see which function is more in line with the requirements. Remember, in C , there is no cross-domain overload - inherited and base classes, although the relationship is very close, this rule cannot be broken. See "The Design and Evolution Of C " or "The C Programming Language" third edition.

However, if you have to have a cross-domain overload, it is not a way to change - you will get those functions to the same domain. Use a using statement to get it.

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;} // .. };

In this way, the result is

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

The overload has occurred - because the sentence in D is clearly telling the compiler, you want to introduce the F in the B domain into the current domain, please compile the compiler "Take the colleagues".

Q: Can I call virtual functions from the constructor?

A: Yes. But you have to go out. When you do this, maybe you don't know what you are doing! In the constructor, the virtual mechanism has not happened because Overriding has not happened. Wanzhang high rise, always start the foundation first? The establishment of the object is also the case - first constructing the base class, and then constructs the ganglion on this basis.

Take a look at this example:

#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 = s;} private: string s;}; int main () {D (" Hello ");} This program is compiled, resulting in this result:

B Constructor b :: f D Constructor

Note that the output is not d :: f. What happened? f () is called in B :: b (). If the rule that calls the virtual function in the constructor is not as previously described, it is as necessary to call D :: f () as some people want. Then because the constructor D :: D () has not yet run, the string S has not been initialized, so when D:: f () is trying to assign the parameter to S, the result is mostly - RMTo.

Conversely, follow the order from inheritance to the base class (demolition of the house, from top to bottom?), It calls the behavior of the virtual function and in the constructor: virtual function is bound at this time. Where (of course, it should be base class - because the inheritance class has been "demolished" - destructure!), Which function is called.

For more details, please see "The Design and Evolution Of C ", 13.2.4.2 or "The C Programming Language" third edition, 15.4.3.

Sometimes this rule is explained in the fact that the compiler is caused. [Translation: From the actual perspective, this can be explained this: In many compilers, the VTABLE is established until the constructor call is completed, and the virtual function is dynamically bind to the same name function of the inheritance class. ] But in fact, it is not such a thing - let the compiler real as a "call virtual function in the constructor" is also the same as the call from other functions "[Demo: Just move the VTABLE to the constructor call That is. The key is also the consideration of language design - let the virtual function can help the general code provided by the base class. [Translation: Do you have eggs before? Bjarne is actually telling you that it is not "first in the actual rule", but "so actually, because the rules are this." ]

Q: Is there "Placement DELETE"?

A: No. But if you really want, you will say - oh, I mean - you can write one yourself.

Let's take a look at the Placement New with a specified place:

Class arena {public: void * allocate (size_t); void deallocate;

// ...};

Void * Operator New (SIZE_T SZ, ARENA & A) {Return A.allocate (SZ); Arena A1 (Some Arguments); Arena A2 (Some Arguments);

Now we can write:

X * p1 = new (a1) x; y * p2 = new (a1) y; z * p3 = new (a2) z; // ... But how do we correctly delete these objects? There is no built-in "Placement Delete" reason that there is no way to provide a universal Placement Delete. The C type system has no way to make us inferred that P1 is the object that is placed in A1. Even if we can know this very gently, a simple pointer assignment operation will also make us trapped. However, the programmer should know what point to what point in his own procedure, so you can have a solution:

Template

Void Destroy (T * P, Arena & a) {if (p) {p-> ~ t (); // Explicit Destructor Call A.deallocate (P);}}

This way we can write:

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

If Arena itself tracks the objects placed, you can safely write the destroy () function, hand it over to Arena, not to bear it.

How to define paired Operator new () and operator delete (), "THE C Programming", "The Design and Evolution Of C ", "The Design and Evolution Of C ", 10.4, and "The C Programming Language, Special Edition, 19.4.5. [Translation: This is translated here according to the original text. As mentioned earlier, "See" The C Programming Language "Third Edition", actually the special edition (Special Edition) and the third edition of the closer copy have no difference. ]

Q: Can I prevent someone from inheriting from my class?

A: Yes, but why? Ok, maybe there are two reasons:

Out of efficiency - I don't want my function calls to be empty for security considerations - Make sure my class is not used as base classes (so I don't have to worry about the object when I copy objects) [Translation note) : "Object Cut" means that when the derived class object is assigned to the base class variable, according to the C type conversion mechanism, only the base class included in the derived class is copied, and the remainder is "cut". ]

According to my experience, "efficiency consideration" is often unnecessary. In C , the virtual function call is so fast, and there is not much difference between the normal function call. Please note that virtual mechanism is only enabled by a pointer or a reference call; if you name a target, the C compiler will automatically optimize and remove any additional overhead.

If you say Byebye in order to and "virtual function calls", then it does have the need for "cap" to the class inheritance system. Before the design, I don't have any questions yourself, why are these functions to be designed. I did see such an example: The demanding function of performance is designed, just because "we are used to do so"!

Ok, no matter what, I have so much, after all, you just want to know, for some reasonable reason, can you prevent others from inheriting your class. The answer is ok. Unfortunately, the solution given here is not clean enough. You have to virtual inheritance of a category category that cannot be constructed in your "capped class". Or let the case tell us everything: 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 Access // usable_lock :: usable_lock (): Private Member MEMBER

(See "The Design and Evolution Of C ", 11.4.3)

Q: Why can't I limit the parameters of the template?

A: Hey, in fact, you can be. And this kind of practice is not difficult, and it doesn't need to exceed routine skills.

Let us see this code:

Template

Void Draw_all (Container & C) {for_each (c.begin (), c.end (), MEM_FUN (& Shape :: DRAW));

If c does not conform to constraints, a type error occurs, then an error will occur in a fairly complex for_each parsing. For example, the type of parameterization is required to instantiate the INT, then we cannot call Shape :: Draw (). The error message we get from the compiler is confusing - because it is unclear with complex for_each in the standard library.

In order to capture this error earlier, we can write code this:

Template

Void Draw_all (Container & C) {

Shape * p = c.front (); // accept Only Containers of Shape * s for_each (c.begin (), c.End (), MEM_FUN (& Shape :: DRAW);}

We noticed that the definition of Shape * P in front (although the program itself, P is useless). If you cannot assign C.front () to Shape * P, then we can get a meaningless error message for most modern compilers. Such skills are common in all languages, and they have to be true for all "unusual structures". [Translation: It means that for any language, when we start to explore the limit, then have to write some highly skillful code. ]

But this is not the best. If you want me to write the actual code, I may write this way:

Template

Void Draw_all (container & c) {typedef typeName Container :: Value_Type T; Can_Copy

(); // Accept Containers of Only Shape * S for_each (C. Segin (), C.End (), MEM_FUN (& Shape :: DRAW));}

This makes the code universal and obviously reflects my intent - I am using assertions [注] TypenAme Container is the type of container accepts that the TypenAme Container is accepted, not confused to define a shape * pointer, I don't know where it will be used later]. CAN_copy () template can be defined as: Template

Struct can_copy {static void constraints (t1 a, t2 b) {t2 c = a; b = a;} can_copy () {void (* p) (t1, t2) = construints;}};

CAN_COPY checks confirmation T1 during compilation T1 can be paid to T2. CAN_COPY

Checking confirmation T is a shape * type, or a pointer to the public inheritance class pointing to the Shape, or the user-defined type of transformation is the type of shape *. Note that the implementation of CAN_COPY () here is basically optimized: a row code to indicate constraints that need to be checked.

[Translation: Refers to the first line of code; constraints is T2], and the type to do this

[Translation: The type of check is t1]; a row code is used to accurately list the consTRAINTS (constraints () function to check if it is satisfied.

[Translation: The second line is not repeated, but there is reason. If T1, T2 are user-defined classes, then t2 c = a; detection can be default; b = a; detection can copy constructs]; a row code is used to provide opportunities to implement these checks

[Translation: Refers to the third line. CAN_copy is a template class; constraints is its member function, and the second line is only defined, not executed.

[Translation: The key to Constraints implementation here is to rely on C powerful type systems, especially the polymorphism of the class. T2 c = a; b = a; B = a; the condition that can be used is: T1 implements T2 interface. Specifically, it may be the following four cases: (1) T1, T2 is the same type (2) Overload Operator = (3) Provide a Cast Operator (Type Conversion Operator) (4) Derivative class object assignment to the base class pointer . Speaking here, I remember that I have said in an article, C Genericity actually --Template does not support constrained genericity, while Eiffel supports constrained genericity from the grammatical level (ie providing similar to template)

As Comparable> XXX Syntax - where Compraready is a constraint. Readers have pointed out that I said that C Template also supports Constrained Genericity. Now this part of this translation gives the use of some techniques, combining OOP and GP methods, thereby homogenerate constrained genericity methods in C . This technique is worthy of fine taste for readers with hobby C . However, don't lose global vision because of the code skills of the various sub-branches. Sometimes lack of language support can make up for more elegant design levels (rather than code levels). In addition, this can be considered "C Template support constrained genericity", I reserve comments. As, use c to use some tips to OOP, but we don't say C language support OOP. ] Please pay attention to it, now our definition has these features we need:

You can express the construints without the definition / copy variables [Declaration: Equivalence Definition / Copy Variable Work is encapsulated in the can_copy template], so that you can do not have to "That type is this initialization", it is not necessary Whether it can be copied and destroyed (unless this is construining). [Translation: ie - unless Constraints is "copied", "can be destroyed". If you use easy-to-understand pseudo code, it is template

XXX, Template

XXX. ]

If you use a modern compiler, Constraint does not bring any additional code definitions or use constraints without using macro definitions If constraint is not met, the error message given by the compiler is easy to understand. In fact, the given error message includes the word "constraints" (so, the encoder can get a prompt from it), the name of Constraints, the specific error reasons (such as "Cannot Initialize Shape * By Double *")

In this case, we don't simply define similar CAN_COPY () or more elegant syntax in the C language itself? The Design and Evolution Of C analyzes the difficulties of this practice. There have been many design philosophy to float the water, just to make the Constraint template class easily write, but also give an error message that is easy to understand when Constraint is not satisfied. For example, I am from "using the function pointer" in can_copy from Alex Stepanov and Jeremy Siek. I think my CAN_copy () does not still have a standardized extent - it needs more practical inspections. In addition, C users encounter many different types of constraints, which seems to have no forms of Constraints to get overwhelming.

There have been many "Built-in Language Support" schemes for Constraints to be proposed and implemented. But actually to express consTraint does not need any exotic thing: After all, when we write a template, we have a strong expression of the C brings us. Let the code testify for my words:

Template

Struct derived_from {static void constraints (t * p) {b * Pb = p;} Derived_From () {void (* p) (t *) = construints;}}; Template

Struct can_copy {static void constraints (T1 a, t2 b) {T2 c = a; b = a;} can_copy () {void (* p) (t1, t2) = constraints;}}; templatestruct can_compare {static void constraints (T1 a, t2 b) {a == b; a! = B; a

Struct can_multiply {static void constraints (T1 A, T2 B, T3 C) {c = a * b;} can_multiply () {void (* p) (T1, T2, T3) = constraints;}}; struct b {} ; Struct D: 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

();} // the classical "elements must derived from mybase *" Constraint: Template

Class Container: Derived_From

{// ...};

In fact, Derived_FROM does not check inheritance, but instead checks convertibility. But Derive_From is often a better name - sometimes giving constraints a good name is also a lot of things that require detail.

Q: We already have "beautiful old QSort ()", why should I use sort ()?

A: For beginners,

Qsort (Array, Asize, SizeOf (Elem), ELEM_COMPARE);

It looks a bit weird. still is

Sort (vec.begin (), vec.end ());

It's better to understand, yes. So, this reason is enough to make you Qsort to pursue sort. For older hands, sort () will make you feel more than Qsort (). Moreover, sort is generic, which can be used for any reasonable container combination, element type, and comparison algorithm. E.g:

Struct record {string name; // ...}; struct name_compare {// compare records sale "name" as the key bool operator () (Const Record & a.10) const {return a.name

& vs) {sort (vs.begin (), vs.end (), name_compare ()); // ...}

In addition, many people enjoy the type of Sort () - to use it without any mandatory type conversion. For standard types, there is no need to write the Compare () function, and there are many people. If you want to see more detailed explanation, see the article "Learning Standard C As a New Language".

In addition, why is sort () fast than QSORT ()? Because it makes it better to use C inline-language semantics.

Q: What is FUNCTION OBJECT?

A: Function Object is an object, but its behavior expresses the image function. In general, it is an object that is instantiated by an Operator () class. The meaning of Function Object is more wide than the usual function, as it can maintain some "status" between multiple calls - this is the same as the static partial variable; but this "state" can also be initialized It can also be detected from the outside, which is stronger than static local variables. Let's take an example:

Class Sum {Int Val; Public: SUM (INT I): VAL (i) {} Operator Int () const {return val;} // extract value int operator () (int i) {return val = i;} / / Application}; void f (Vector

v) {SUM S = 0; // Initial value 0 s = for_each (v.begin (), v.end (), s); // Gather the sum of all elements cout << "the sum is" << S << "/ n"; // or even: cout << "The Sum IS" << for_each (v.begin (), v.end (), sum (0)) << "/ n";}

Here I would like to pay attention to you: A Function Object can be in linenizing, because for the compiler, there is no annoying pointer to confuse the audio, so this optimization is easy. [Translation: This means that Operator () is defined as an inline function, which can increase efficiency. As contrast, the compiler is almost impossible to save the "Through Function Normal Rejection Function" step by optimization, at least in this.

In the standard library, Function Objects is widely used, which brings great flexibility and scalability to the standard library.

[Translation: C is a language of Bozi, the concept of Function Object is borrowed from Functional Programming; and the powerful and expression of C itself makes this "brought" possible. In general, a function pointer can also be used in places using Function Object; we often use pointers when we are not familiar with Function Object. But the syntax defined by a function pointer is not too simple, and the pointer to the C has long been behind the "wrong source". What's more, the indirect overhead is added by the pointer invoking function. Therefore, whether it is for grammar or efficiency, it should be advocated to use Function Objects.

Below we will understand the Function Objects from the angle of the design mode: This is a typical application of Visitor mode. When we want to apply some kind of operation to a / some object, do not want to limit this operation, then Visitor mode can be used. In the Design Patterns book, the author uses this model as: This operation is provided through a Visitor class (in the previous Bjarne Stroustrup code, SUM is a Visitor variant), instantiate a Visitor object with Visitor classes. (Of course, in the previous code is S); then call Visitor.visit () for each object during the iterator iteration. Here VISIT () is a member function of the Visitor class, which is equivalent to the "special member function" in the SUM class --Operator (); Visit () can also be defined as an inline function to remove indirectness, improve performance. Call here, the reader pays attention to the C regards the overloaded operator as a function, but it is only a function with a special function name. So actually demonstrate the demonstration of the Visitor mode in the DESIGN PATTERNS book and the practical object of Function Object is substantially equivalent. A Function Object is also a special Visitor. ] Q: How should I handle memory leakage?

A: It is very simple, just write "unlike" code. Obviously, if your code is New, Delete, a pointer operation, then you want it to "no leak" is difficult. No matter what you are cautious, Jun is people, non-god, and the mistake is inevitable. Eventually you will be mad by your own increasingly complex code - you will be involved in the struggle with memory leaks, don't leave for Bug, until the mountain does not have angular, and the earth will no longer turn it. It is not complicated to avoid such a difficult trick: you just rely on implicitly implicitly distributed mechanisms behind the scenes - constructors and destructors, let C 's powerful class system to help you have an arm. The containers in the standard library are very good examples. They make you don't have a lot of time energy to manage memory and easily manage memory. Let's take a look at the sample code below - imagine if there is no string and vector, what will the world will? If you don't need them, can you write the same function code that does not have a memory error in the first time?

#include

#include

#include

#include

Using namespace std; int main () // small program message around with strings {cout << "Enter Some Whitespace-Separated Words: / N"; Vector

v; string s; while (cin >> s) v.push_back (s); sort (v.begin (), v.end ()); string cat; typedef multi

:: const_iterator it ;; for (iter p = v.begin (); p! = v.end (); p) Cat = * P " "; cout << cat << '/ n';}

Please note that there is no explicit memory management code. No macro, no type conversion, no overflow detection, no mandatory size limit, no pointer. If you use the Function Object and the Standard Algorithm [Translation: Itel Algorithm provided in the Indicator], I can even don't use iterator. However, this is just a small program, killing chicken is used with a cow knife? Of course, these methods are not impeccable, and it is easy to use to use them to use them to use them. However, in any case, their broad applicability is surprising, and they do enhance the readability and manageability by removing a large number of explicit memory allocation / release code. As early as 1981, I pointed out that the number of objects that need to be explicitly managed, using C "will be" more difficult "will no longer be an extremely feminine arduous task.

If your application is not able to help you a class library in memory management, then if you still want your software development, it is easy to get the right result, it is best to build such a library first.

If you can't let memory allocation and release become an object's "natural behavior", you can at least avoid memory leakage by using the resource handle. Here is an example: Suppose you need to return an object from the function, this object is assigned on the free memory stack; you may forget to release that object - after all we can't make the pointer to determine if the object does need to be released We can't know who should be responsible for releasing it. So, use the resource handle. For example, the auto_ptr in the standard library can help clarify: "Release Object" Who is the responsibility? Let's see:

#include

#include

Using namespace std; struct s {s () {cout << "Make AN S / N";} ~ s () {cout << "deStroy An S / N";} s (const s&) {cout << " Copy Initialize AN S / N ";} S & Operator = (const S &) {cout <<" Copy Assign AN S / N ";}}; s * f () {

Return New S; // WHO Is Responsible for DELETING THIS S?}; auto_ptr g () {Return Auto_PTR (New S); // Explicitly Transfer Responsibility for Deleting this s} int main () {cout << "start main / n "; s * p = f (); cout <<" after f () Before g () / n "; // s * q = g (); // caught by compiler auto_ptr q = g (); cout << "exit main / n"; // Leaks * p // implicitly deletes * q}

Here is just examples of memory resource management; as for other types of resource management, it can be made such as the method. If this method cannot be used in your development environment (for example, you have used the antique code provided by the third party, or ancient "caveman" participated in your project development), then you can be in the development process. Tenship should remember to use the memory leak detection program, or simply use the garbage collection.

Q: Why can't you continue to execute the following code after you can't continue to perform this problem? carried out? [Translation: For example, a simple resume statement, usage and existing returnitist are similar, but must be placed at the end of Exception Handler. ]

Well, from the abnormal processing code to the abnormality, the idea of ​​continuing the following code is very good [Translation] The design of the current exception mechanism is: When the exception is thrown and processed, the CATCH block where the processing code is located. Implementation], but the main problem is that it is impossible to know how much to clear an abnormality in order to make the back code is running normally. After all, when there is an abnormality, things are a bit uncomfortable, isn't it? What's more, you can get a troublesome thing to pick up your rotten stalls. So, if you want to let "continue" can work properly, write the Throw code and write the Catch code must be familiar with each other, and this brings Complex mutual dependencies [translation] refers to "interdependence" between developers, but also refers to the interdependence of the code - tight coupling code is not a good code: o)], it will bring a lot of trouble Maintenance problem. When I design C 's abnormal handling mechanism, I have seriously considered this issue; this problem is also discussed in detail in the process of C standardization. (See chapter "THE DESIGN AND EVOLUTION OF C " chapter about exception processing) If you want to try to resolve the problem before throwing an exception, then continue to execute, you can call a "check-recovery" function, then If you still can't solve the problem, throw an exception. One such example is new_handler.

Q: Why don't C do not have a reference to a reference to a realloc ()?

A: If you want to want, you can of course use Realloc (). However, realloc () only assigned by Malloc () C function "cooperation is pleasant", and there is no object with a user-defined constructor in allocated memory. Keep in mind: In contrast to the imagination of some innocent people, realloc () is necessary to copy a large amount of memory to the newly allocated continuous space. So, realloc is nothing good ^ _ ^

In C , there is a better way to handle memory, such as containers in the standard library, such as Vector. [Translation: These containers will manage the needs of the required memory, and "Growth Size" - is redistributed when necessary. ]

Q: How do I use an abnormality?

A: See "THE C Programming Language" 14 8.3, and Appendix E. Appendix E mainly explains how to write "Exception-Safe" code, this appendix is ​​not written to beginners. A key technique is "Resource Allocation," - This technique brings "the dawn" of "order" to the "resource management" that is easy to cause confusion.

Q: How do I read String from standard input?

A: If you want to read a single word with blank, you can do this:

#include

#include

Using namespace std; int main () {cout << "please enter a word: / n"; string s;

CIN >> S; cout << "YOUED" << S << '/ n';} Please note that there is no explicit memory management code here, and there is no limit size and may accidentally overflow buffers. [Translation: It seems that Bjarne is often proudly claimed - because this is one of the major benefits of String and even the entire standard library, it is really proud; and in the old C language, the most complained of the programmer is also built-in characters. The lack of string types and the troubles caused by the "complex memory management measures required for operation strings". Bjarne must think about it, "Ha, my small Baby called C is finally greater, it is perfect!": O)]

If you need to read a whole time, you can do this: #include

#include

Using namespace std; int main () {cout << "please enter a line: / n"; string s;

GetLine (CIN, S); cout << "you entered" << S << '/ n';}

For a profile (such as iostream, stream) provided on the standard library, see Chapter 3 of the Third Edition of The C Programming Language. If you want to see the specific comparison of the input and output features of C and C , see "Learning Standard C As a New Language".

Q: Why doesn't C do not provide "Finally" structure?

A: Because C provides another mechanism, it can replace Finally, and this mechanism is almost always better than Finally: "Allocate resources, initialization". (See "The C Programming Language" 14.4) The basic idea is to encapsulate a resource with a local object, so that the destructor of the local object can automatically release the resource. In this way, the programmer will not "forget to release resources". [Translation: Because the C object "Lifecycle" mechanism remembers him: o)] The following is an example:

Class file_handle {file * p; public: file_handle (const char * n, const char * a) {p = fopen (n, a); if (p == 0) throw open_error (errno);} file_handle (file * pp ) {p = pp; if (p == 0) throw open_error (errno);} ~ file_handle () {fclose (p);} Operator file * () {Return P;} // ...}; void f (const char * fn) {file_handle f (fn, "rw"); // open file through f}

In one system, each resource requires a "resource link" object, but we don't have to write a "finally" statement for each resource. In the actual system, the number of resources acquisition and release is far more than the type of resource, so the code generated by the "Resource Assignment," The code generated is less than the "FINALLY" mechanism.

[Translation: Object Pascal, Java, C # and other languages ​​have a Finally statement block, which is often used to handle resources for the resource allocated in an abnormality - this means how many Finally statements have a FINALLY statement (less than one Finally means having some resource allocation is not "Exception Safe"); and the "resource allocation, initialization" mechanism moves the code originally placed in the Finally block to the sectual function. We only need to provide a package class for each type of resource. How old is the amount of code? Unless each type of resource is only used once - this situation is equal; otherwise it will always be more than the latter: o)] In addition, please see "The C Programming Language" appendix E - resource management examples.

Q: What is the auto_ptr? Why is there auto_Array?

A: Oh, auto_ptr is a very simple resource package class, it is

The header file is defined. It uses "resource allocation, initialization" technology to ensure that resources can be securely released when an exception occurs ("Exception Safety"). A auto_ptr encapsulates a pointer or is used as a pointer. Auto_PTR will automatically release the pointer when its life cycle is at the end. E.g:

#include

Using namespace std; struct x {int m; // ..}; void f () {auto_ptr

P (new x); x * q = new x; p-> m ; // use p Just Like a Pointer Q-> m ; // ... delete q;

If the exception is thrown in the area where the code is // ..., then P will be deleted normally - this credit should be recorded on the destructor of Auto_PTR. However, Q pointed to the X type object is not released (because it is not defined by auto_ptr). For details, please see "The C Programming Language" 14.4.2. Auto_ptr is a lightweight class without introducing a reference counting mechanism. If you assign an auto_ptr to another Auto_PTR (for example, AP2), the AP2 will hold the actual pointer, and the AP1 will hold a zero pointer. For example: #include

#include

Using namespace std; struct x {int m; // ..}; int main () {auto_ptr

p (new x); auto_ptr

Q (p); cout << "p" << p.get () << "q" << q.get () << "/ n";}

The run results should be a zero pointer first, and then an actual pointer is like this:

P 0x0 q 0x378d0

Auto_ptr :: get () Returns the actual pointer.

Here, semantics seem to be "transfer", not "copy", which may be a bit surprising. It is important to note that do not use Auto_Ptr as a standard container parameter-standard container requires usually copy semanties. For example: std :: vector

> v; // error

A auto_ptr can only hold a pointer to a single element instead of array pointer: void f (int N) {auto_ptr

p (new x [n]); // error // ...}

The above code will be wrong because the destructor is used to release the pointer using delete instead of delete [], so the N-1 X behind is not released. So, it seems that we should use DELETE [] to release the pointer, called Auto_Array's similar stuff to place it? Oh, no, no, there is no auto_Array. The reason is that there is no need. - We can use Vector: void f (int N) {Vector

v (n); // ...}

If an exception occurs in // ..., the destructor of V is automatically called.

Q: C and C style memory allocation / release can be mixed?

A: Yes - From you can use Malloc () and New's sense in a program.

No - From the sense of DELETE, a malloc () allocation, the object of malloc (). You cannot free () or realloc () an object that is allocated by New. C New and Delete operators ensure that structures and sectations are normal, but the C-style malloc (), Calloc (), free () and realloc () may not guarantee this. Moreover, no one can guarantee you, the memory controlled by New / Delete and Malloc / Free is "compatible". If in your code, the two style is mixed without causing trouble, then I can only say: until now, you are very fortunate: o) If you miss "Beautiful old realloc ()" (many People miss her) and cannot cut the entire ancient C memory distribution mechanism (love house and Wu?), Consider using the VECTOR in the standard library. For example: // read Words from Input Into a Vector of Strings: Vector

Words; string s; while (cin >> s && s! = ") Words.push_back (s);

The Vector will automatically grow as needed.

Other examples are given in the article "Learning Standard C AS A New Language", you can refer to.

Q: I want to convert from void *, why must I use a transition? A: In C, you can implicate, but this is not safe, for example: #include

INT main () {char i = 0; char J = 0; char * p = & i; void * q = P;

INT * PP = Q; / * Unsafe, Legal C, NOT C * / Printf ("% D% D / N", I, J); * PP = -1; / * Overwrite Memory Starting AT & I * / Printf ( "% D% d / n", i, j);}

If you use the T * type pointer, the pointer does not point to T type objects, the consequences may be catastrophic; so if you want to change the void * in C , you must use explicit replacement:

INT * PP = (int *) q;

Alternatively, it is better to use a new transform, so that the replacement operation is more eye-catching:

INT * PP = static_cast

(Q);

Of course, the best is still - don't change.

When the most common insecurity of Class C occurs when the memory allocated by malloc () is assigned to a pointer, for example: int * p = malloc (sizeof (int)); in C , it should Use the type of secure New operator:

INT * P = new int;

Moreover, New has an accompanying benefits:

New does not "accidentally" allocate the error size memory New Automatically check if the memory has been exhausted New Support initialization

E.g:

Typedef std :: Complex

CMPLX; / * c style: * / cmplx * p = (cmplx *) malloc (intend); / * error: wrong size * / / * forward to test for p == 0 * / if (* p = = 7) {/ * ... * /} / * OOOOPS: Forgot To Initialize * p * / // c style: cmplx * q = new cmplx (1, 2); // Will throw bad_alloc if Memory is Exhausted IF (* q == 7) {/ * ... * /}

A: How to define a constant in the class? Q: If you want to get a constant that can be used in constant expressions, such as array size definition, then you have two options: Class X {Static Const Int C1 = 7; Enum {C2 = 19}; char V1 [C1] CHAR V2 [C2]; // ...}; look at it, the definition of C1 seems to be more straightforward, but don't forget that only STATIC's integer or enumeration amount can be initialized. This is very limited, for example: Class Y {Const Int C3 = 7; // error: not static static int C4 = 7; // error: not const static const float c5 = 7; // error not integral}; I still prefer to play "Enum Drapest" because this definition is well portable, and will not attract me to use non-standard "category initialization" extended syntax. So why do you have these inconvenient restrictions? Because classes are usually declared in header files, the header file is often included in many units. [So, the class may be repeatedly declared. ] However, in order to avoid the complexity of the linker design, C requires that each object can only be defined once. If the C allows the class to define the entity in memory as an object, then this requirement cannot be satisfied. Some compromises on C design, see "The Design and Evolution Of C ". If this constant does not need to be used for constant expressions, then your choice is more room for it: Class Z {static char * p; // initialize in definition constor: // initialize in constructor public: z (int II : i (ii) {}}; char * z :: p = "Hello, There";

Only when Static members are defined outside the class, you can get its address, for example:

Class ae {// ... public: static const INT C6 = 7; static const INT C7 = 31;}; const INT AE :: C7; // definition int f () {const * p1 = & ae :: C6 ; // error: c6 not an LVALUE const INT * P2 = & ae :: c7; // ok // ...} Q: Why does the delete operation does not block a pin?

A: Well, ask quite reasonable. Let's see:

Delete P; // ... delete P;

If the // ... portion in the code is not given to the P allocated memory again, then this code is released twice to the same memory. This is a serious mistake, but unfortunately C can't effectively prevent you from writing this code. However, we all know that the release of the empty pointer is unharacle, so if you are in every delete p; back is followed by P = 0; then the error that releases the same memory will not happen twice. Despite this, there is no syntax in C to enhance the programmer to return the pointer immediately after the release pointer. Therefore, it seems that the heavy responsibility to avoid such a mistake can only be all over the shoulders of the programmer. Perhaps, DELETE automatically zero the pointer is a good idea?

Oh, no, this idea is not "good". One reason is that the delete's pointer is not necessarily left value. Let's look at: delete p 1; delete f (x);

What is you let DELETE automatically zero? Perhaps this example is not common, but the foot can prove that "Delete automatically returns to zero" is not insurance. [Translation: In fact, we really want is: "Any pointer to the released memory area is automatically returned" - but unfortunately, there is no stuff in addition to Garbage Collector. ] Let's take a simple example:

T * p = new t; t * q = p; delete p; delete q; // ouch!

The C standard actually allows the compiler to "automatically block the left value of the DELETE", I also want the compiler manufacturer to do this, but it seems that the manufacturer doesn't like this. One reason is the above example - the third line of statement If the delete is automatically set to zero? q is not automatically set, the fourth line of appearance is wrong.

If you feel that the needle is zero when you release the memory, then you can write such a Destroy function: Template

Inline void destroy (t * & p) {delete p; p = 0;

Will see the trouble brought by Delete as another reason to use New / Delete, the container in the standard library ": o) Please note that the pointer is passed as a reference (so that Delete can zero the needle to zero) Bring additional benefits - prevent the right value from being passed to destroy (): int * f (); int * p; // ... destroy (f ()); // error: Trying to pass an rvalue by Non -const Reference Destroy (P 1); // Error: Trying to Pass An Rvalue by Non-Const Reference

Q: Can I write "void main ()"?

A: Such definition

Void main () {/ * ... * /}

Not C , nor C. (See ISO C Standard 3.6.1 [2] or ISO C Standard 5.1.2.2.1) A compliance compliance compiler should accept int Main () {/ * ... * /}

with

INT Main (int Argc, char * argv []) {/ * ... * /}

The compiler can also provide more overload versions of main (), but they must return int, this int is the caller of returning to your program, this is a "responsible" practice, "nothing is returned" Can not be good. If your program's caller does not support communication with the Return Value, this value will be automatically ignored - but this does not allow Void main () into legitimate C or C code. Even if your compiler supports this definition, it is best not to develop this habit - otherwise you may be shallow ignorant by other C / C .

In C , if you are troublesome, you may not have to explicitly write Return statements. The compiler will automatically return 0. E.g:

#include

INT main () {std :: cout << "this program Returns the integer value 0 / n";}

Is it trouble? Not in trouble, int main () is less than void main (): o) In addition, please pay attention: Whether ISO C is also C99, you will not allow you to omit the return type definition. This is to say, and C89 and ARM C [Demolition: Normally, the C ] described in the "The Annotated C Reference Manual" in 1990 is different from 1990, INT is not the default return value. and so,

#include

Main () {/ * ... * /}

Will be wrong because the main () function lacks the return type.

Q: Why can't I overload ".", "::" and "sizeof" and other operators?

A: Most of the operators can be overloaded, exceptions only ".", "::", "?:" and "sizeof". Nothing is not forbidden for Operator ?: Rease, but there is no need. In addition, the overload function of EXPR1 "EXPR2: EXPR3 cannot guarantee that only one of EXPR2 and EXPR3 is executed.

And "SIZEOF" cannot be overloaded because there are many internal operations, such as pointer plus method, depending on it, for example: x a [10]; x * p = & a [3]; x * q = & a [3]; P ; // p Points to a [4] // thus the INTEGER VALUE OF P MUST BE // Sizeof (x) Larger Than THE INTEGER VALUE OF Q

In this way, SIZEOF (X) cannot express what new semantics without violating basic language rules.

In n :: m, N and M are not expressions, they are just the name of the compiler "know", "::" The actual operation of "::" is the name domain parsing at compile, and there is no expression of the operation involved. . Perhaps someone will feel overloaded a "x :: y" (where X is the actual object, not the name domain or class name) is a good idea, but this is introduced into the new syntax [Translation note: The intention of the overload is to let Operators can have new semantics, rather than changing syntax - otherwise confusion], I don't think that the complexity brought by the new grammar will give us what benefits. In principle, "." The operator can be overloaded, just like "->". However, this will bring a semantic confusion - we are thinking about and "." The object deal is dealt with, or "." The actual entity actually pointed to the back of the end? Take a look at this example (it assumes "." Overload is ok): Class Y {public: void f (); // ...}; class x {// Assumert you can overload. Y * p; y & Operator. () {return * p;} void f (); // ...}; void g (x :: f or y :: f or error?} There are several solutions. When C is standardized, what solutions are not obvious. See "The Design and Evolution Of C " for details. Q: How can I convert an integer into a string?

A: The easiest way is to use StringStream:

#include

#include

#include

Using Namespace Std; String Itos (INT I) // Convert Int To String {StringStream S;

S << i; return s.str ();} int main () {INT i = 127; string ss = itos (i); const char * p = ss.c_str (); cout << SS << " << p << "/ n";}

Of course, naturally, you can use this method to convert any types of "<<" output to String. Want to know the details of the String stream? See "THE C Programming Language", Section 21.5.3.

Q: "INT * P;" and "int * p;", which one is correct?

A: If you let your computer read, both are completely equivalent, it is correct. We can also declare "int * p" or "int * P". The compiler will not pay attention to where you put a few spaces.

However, if people read, the meaning of the two is different. The writing style of the code is very important. C styles of expression and declaration are often considered to be more than "Necessary Evil" [translation: "necessary evil", means that in order to achieve a certain purpose and have to pay. For example, some people think that the environment is destroyed by the "Necessary Evil"] of the economic development, and C emphasizes the type. Therefore, "INT * P" and "int * p" are not right, only the style of style. A typical C programmer will write "int * p", and the vibration is telling you "This means that '* p is an int'" - it is quite reasonable. Here, * and p are tied together - this is the style of C. This style emphasizes grammar. And a typical C programmer will write "int * p", and tell you "P is a pointer to the int, INT *". This style emphasizes type. Of course, I like this style: O) and, I think that type is very important concept, we should pay attention to the type. Its importance is not as "more advanced parts" in the C language. [Translation], such as RTTI, various CAST, TEMPLATE mechanism, etc., can be called "higher levels", but they are actually extension and use of type concept. I have written two articles talk about C and OOP published in this journal, and the text emphasizes the importance of understanding "type". I have also translated Object Unencapsuated (this book is called by the author's widely circulated C ?? a critique revised), the author of this book is even called Object Oriented Programming should be named. Type Oriented Programming - "Programming"! This is a bit overkill, but the type is indeed a core part of the programming language. ] When declares a single variable, INT * and INT * are not particularly prominent, but when we have to declare multiple variables, it is easy to confuse: int * p, p1; // probable error Error : p1 is not an int * Here, the type of P1 is int or Int *? Put the * to P nearly a point, the problem cannot be clarified:

INT * P, P1; / / PROBABLE ERROR?

It seems that in order to be insured, I have to declare a variable once - especially when the statement is accompanied by initialization. [Translation: This FAQ is a declaration for the translation of DECLARE / DECLAration; define / definition is translated into definitions. It is generally believed that the basic differences between the two are: "declaration" is only providing information for the compiler, allowing the compiler to reserve positions (such as type names, variable names, function names, etc.) in the symbol table, not Specifically, the specific semantics corresponding to the symbol - ie: no distribution of memory spaces or the generation of actual binary code. And "Definition" must refer to the semantic - if the "declaration" is compared to a new word for a new word; then "definition" is like this word to this word, and the usage gives a detailed explanation. When we say a C statement is "defined", the compiler will definitely generate the corresponding machine instruction or allocate memory, and the statement called "declaration" will not be compiled any actual code. From this point of view, some places in the original article are "objects, classes," declarations ", but may translate to" definition "more in line with our understanding. However, this translation is also a translation of loyalty to the original text and does not change in accordance with my understanding. This explanation. In addition, translations involving my individual's understanding of the original text, and the supplements are given in the form of the translation. ] People are generally unlikely to write code like this: int * p = & i; int p1 = p; // error: int initialized by int *

If someone is really written, the compiler will not agree - it will report an error.

Whenever there are two or more ways to reach a certain purpose, some people will be confused; whenever some choices are for personal preferences, the argument will have no stop. I insisted on only a pointer and in the statement, it will happen to the source of our confusion. If you want to know more about C's declaration syntax, see "The Design and Evolution Of C ".

Q: What code layout style is good?

A: Oh, this is a personal taste problem. People often attach great importance to the style of the code layout, but the consistency of the style is more important than what is selected. If you want to build a "logical certificate" for my personal preference, like someone else, I will head: o)

I personally like to use the "K & R" style, if it is calculated that the use of the constructs do not exist in the C language, then people are sometimes referred to as "Stroustrup" style. For example: Class C: public b {public: // ...}; void f (int * p, int max) {if (p) {// ...} for (int i = 0; i

This style is more saved "vertical space" - I like to let the content as much as possible can be displayed on one screen: o), the reason why the function definition begins to place, because this will be separated from the class definition. Come, I can see it at a glance: Oh, this is a function!

The correct indentation is very important. Some design problems, such as using an abstract class to represent an important interface, using templates to represent flexible and scalable types of security abstraction, correctly use "exception" to represent errors, far more than code style. [Translation: "The Practice Of Programming" is explained in detail the "code style" problem. ]

Q: Which should be written in front of the type?

A: I like to write it in front. However, this is just a problem with personal taste. "Const T" and "T Const" are allowed and they are equivalent. E.g:

Const int A = 1; // ok int covest b = 2; // Also ok I think, use the first way to write more language habits, it is not easy to confuse: o)

Why is this this? When I invent "const" (earlier is named "Readonly" and there is a corresponding object called "Writeon"), I let it go in front and back, because this will not bring zoneiness. The C / C compiler at the time had little sequence rules for modifiers. I don't remember what I have had something about the contest or related arguments. Some early C users (especially I) were simply simply think of const Int c = 10; compared to int const c = 10; good look. Perhaps, I am an impact of this fact: Many examples I wrote in the early years are modified with "readonly", and readonly int C = 10; indeed look more than int = 10; comfortable. The earliest use "const" C / C code is that I use the global search replacement function to replace ReadOnly to const. I still remember that I discussed the "variant" problem with grammar, including Dennis Ritchie. However, I don't remember what kinds of languages ​​we talked at the time. Also, please note: If the pointer itself cannot be modified, then consT should be placed behind "*". For example: int * const p1 = q; // constant pointer to int variable int const * p2 = q; // POINTER TO Constant Int const * p3 = q; // Pointer to constant int

Q: What is the macro?

A: Macro does not follow the role of C and type rules, which will bring a lot of trouble. Therefore, C provides alternative mechanisms that can be "collaborate" in other parts of the language, such as inline functions, templates, name spatial mechanisms. Let us look at this code:

#include "someheader.h" struct s {int alpha; int beta;}; if someone wrote a macro called "alpha" or "beta", then this code cannot be compiled, and may even be more Worse - compiles some results you have never expected. For example: if "someHeader.h" contains the following definition: #define alpha 'a' #define beta b [2] Then the previous code is completely deviated from it.

All the names of the macro (and only macro) are indeed helpful to relieve problems, but macros have no language-level protection mechanism. For example, in the above examples, Alpha and Beta are in the scope of S, the member variables of S, but this has no effect on macro. The development of the macro is conducted before compiling, and the expansion program only regards the source file as a character flow. This is also the lack of the C / C programming environment: the meaning of the source file in the computer and computer eyes is different.

Unfortunately, you can't make sure that other programmers do not make the "stupid" mistake you think. For example, someone recently told me that they encountered a macro with "goto" statement. I have seen such a code and I have heard such an argument - "Goto" sometimes is useful. For example: #define prefix get_ready (); int RET__ #Define return (i) ret__ = i; do_something (); goto exit #define suffix exit: cleanup (); returnitch () {prefix; // ... Return (10); // ... Return (x ); // ... suffix;} If you are a programmer responsible for maintenance, such code is submitted to you, and the macro definition (to give this " The drama "increased difficulty) is hidden in a header file (this situation is not rare), what do you think? Is it a fog?

A common and subtle problem is that the macro of the function style does not comply with the function parameter call rules. For example: #define square (x) (x * x) Void f (double D, INT i) {Square (D); // Fine Square (i ); // Ouch: Means (i * i ) Square (D 1); // Ouch: Means (D 1 * D 1); That IS, (D D 1) // ...}

"D 1" problem can be resolved by giving macro definitions:

#DEfine Square (x) ((x) * (x)) / * better * /

However, the issue of "i " is not resolved twice.

I know that something (other language) is called "macro" is not like the "macro" defective in the C / C preprocessor, and it is more troublesome, but I don't want to improve the macro of C , but suggestions. You correctly use other mechanisms in the C language, such as inline functions, templates, constructor, destructive functions, exception handling, and more.

[Translation] The full translation of the C Style and Technique FAQ of Bjarne Stroustrup. Bjarne is Danish, and his English article can be read, and the technical article is particularly. This translation may be a lot of mistakes, and the readers are welcome to correct. My email is zMelody@sohu.com. ]

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

New Post(0)