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

zhaozj2021-02-16  112

Bjarne Stroustrup's FAQ: C style and skills

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 It can only be effective.

Interpreter of HTML Format See Transfer Home Page: http://www.wuseuang.net/

If you have any comments and suggestions about this translation, please send a letter to the translation: onekey@163.com.

The address of the original text is: http://www.research.att.com/~bs/bs_faq.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).

table of Contents:

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 define a constant of in-class?

Why does Delete not set an operation?

Can I write "void main ()"?

Why can't I overload symbols, ::, sizeof, and so on?

How to convert a integer value into a string?

"INT * P" is correct or "int * p" correct?

Which layout style is the best for my code?

Should I put "const" or after the type?

What is the problem with using the macro?

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. This issue has a very representative solution, that is (in your program), read several numbers, do some processing, and output the result. 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; // Successfully returned

}

Observation on this program:

This is a standard ISO C program that uses a standard library (STANDARD LIBRAR). 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); // Read an element

IF (! cin.eof ()) {// Check if the input failed

cin.clear (); // Clear error status

String S;

CIN >> S; / / Find end characters

IF (s! = "end") {

CERR << "Format Error / N";

Return 1; // Return to errors

}

}

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

Reverse (v.begin (), v.end ());

COUT << "Elements in Reverse Order: / N";

For (int i = 0; i

Return 0; // Successfully returned

}

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: // User's interface using Shapes

Virtual void Draw () const;

Virtual Void Rotate; Int De Derees

// ...

Protected: // Common Data (for Implementers of Shapes)

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, the interface is indicated by the abstract base class: Class Shape {

Public: // User's interface using 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 (int) {}

Point center () const {return center;}

// ...

protected:

Point cent;

Color color

int RADIUS;

// ...

}

Class Triangle: Public Shape {

PUBLIC:

Void Draw () Const;

Void Rotate (int);

Point center () const;

// ...

protected:

Color color

Point A, B, C;

// ...

}

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: // User's interface using 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

// ...

}

Class Circle: Public Shape, Protected Common {

PUBLIC:

Void Draw () Const;

Void rotate (int) {}

Point center () const {return 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;

}

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 a separate byte.

Struct x: Empty {

Int a;

// ...

}

Void f (x * p)

{

Void * p1 = p;

Void * p2 = & p-> a;

IF (p1 == p2) cout << "nice: good Optimizer";

}

This optimization is allowed, which can be widely used. It allows programmers to use empty classes to express some simple concepts. Some compilers are now 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 the complex number:

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, multiple 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 Derived: public base {

// ...

~ Derived ();

}; void f ()

{

Base * p = new deerid;

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 creates a function of 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 (); // uses A as the appropriate type

B * q = fac.make_a_b (); // Type B as the appropriate type

// ...

}

Struct fx: f {

A * make_an_a () const {return new ax ();} // ax is a derived

B * Make_a_b () const {return new bx ();} // AX is the derived branch

}

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 B's derived

}

int main ()

{

User (fx ()); // This user establishes AX and BX

User (fy ()); // This user creates AY with 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 i) {cout << "f (int):"; Return i 1;}

// ...

}

Class D: public b {

PUBLIC:

Double F (Double D) {cout << "f (double):"; Return D 1.3;}

// ...

}

int main ()

{

D * pd = new d;

COUT << PD-> F (2) << '/ n';

Cout << PD-> f (2.3) << '/ n';

}

The result of its output is:

f (double): 3.3

f (double): 3.6

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

check it out:

#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 ("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 by 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.

check it out:

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.begin (), 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 Typename Container :: Value_Type T;

CAN_copy (); // ACCEPT Containers of Only Shape * S

For_each (c.begin (), c.end (), MEM_FUN (& Shape :: Draw));

}

This is very clear, I am building an assertion. 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 to check

One row lists the constraints to be checked (constructs () functions)

A method of providing 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 case where the constraint is to check these attributes.)

Use the current compiler, do not need to generate code for constraints

Define and use constraints, do not need to use macros

When the constraint fails, the compiler gives acceptable error messages, including "constraints" (gives 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 . check it out:

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) = constructs;

}

Template Struct CAN_Compare {

Static void constraints (t1 a, t2 b) {a == b; a! = b; a

CAN_Compare () {Void (* P) ​​(T1, T2) = construints;}};

Template Struct Can_Multiply {

Static void constraints (t1 a, t2 b, t3 c) {c = a * b;}

CAN_MULTIPLY () {void (* p) (T1, T2, T3) = constructs;

}

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.

Since there is already an excellent QSort () function, why still need a sort ()?

For beginners,

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

It looks too ubous, and it is more difficult to understand:

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

For experts, Sort () is faster than QSort () in the same case of Comparison Criteria, which is very important. Moreover, qsort () is universal, so it can be used for any meaningful combination of different container types, element types, and compare methods. for example:

Struct Record {

String name;

// ...

}

Struct name_compare {// uses "name" as the key comparison RECORD

Bool Operator () (Const Record & A, Const Record & B) Const

{Return A.Name

}

Void F (Vector & VS)

{

Sort (vs.begin (), vs.end (), name_compare ());

// ...

}

Moreover, many people appreciate sort () because it is safe, using it does not need to make shape (CAST), no one must write a Compare () function for the basic type.

For more details, see my article "Learn Standard C as a new language" (Learning C As a new language), you can find it from my list of articles. Sort () The main reason for Qsort () is that comparison operations are doing better in Inlines.

What is a function object (Function Object)?

As the name suggests, it is an object that is like a function in some way. Typically, it refers to an instance of a class, which defines the application operator Operator ().

Function objects are more common than functions because function objects can define a sustainable portion (similar to static partial variable) across multiple calls, while it can initialize and check from the outside of the object (and different local variables). E.g:

Class Sum {

Int Val;

PUBLIC:

SUM (INT I): VAL (i) {}

Operator int () const {return val;} // get 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); // ask for all elements and

Cout << "The sum is" << S << "/ n";

// or even:

COUT << "" the suuse "<< for_each (v.begin (), v.end (), sum (0)) <<" / n ";

}

Note that a function object with an application operator can be perfectly inline because it does not involve any pointer, the latter may result in rejecting optimization. In contrast to it is that the existing optimizer is hardly (or completely unable to?) Inlined one by the call through the function pointer.

In the standard library, the function object is widely used to obtain flexibility.

How should I deal with memory leak?

Writing code that does not cause any memory leaks. Obviously, when you are full of new operations, DELETE operations and pointer operations in your code, you will fell in a place, causing memory leaks, pointers, and what is like this. This is to carefully treat memory allocation work. It is completely indistinguishable: The complexity of the code will eventually exceed the time and effort you can pay. So subsequently produced successful skills, depending on the type of memory allocations and reassignment (DEALLOCATION) work hidden after easy management type. Standard Containers is an excellent example. They don't manage memory by you but I have a memory, thus avoiding bad results. Imagine, there is no help and vector help, write this:

#include

#include

#include

#include

Using namespace std;

INT main () // small programmeing arround 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 Vector :: Const_Iterator.

For (iter p = v.begin (); p! = v.end (); p) Cat = * P " ";

COUT << Cat << '/ n';

}

How many opportunities do you get the correct result in the first time? How do you know that you didn't cause memory to leak?

Note that there is no explicit memory management, macro, shape, overflow check, explicit length limit, and pointer. By using a function object and standard algorithm, I can avoid using pointers, for example, using iterators, but for a small program for a small program.

These techniques are not perfect, and they are not always so easy to systematically use them. However, applying them produces an amazing difference, and by reducing the number of explicit memory allocations and reassignment, you can even make the remaining example easier to trace. As early as 1981, I pointed out that by reducing the number of objects that I must explicitly track from tens of thousands of hits, in order to make the program's correct operation, I have become a terrible work, turning into some Managed objects, even more simple.

If your program has not included the reduction of explicit memory management to the minimum library, then let your program complete and correctly run, the fastest way may be the first to build a library first.

Templates and standard libraries realize containers, resource handles, and so on things, earlier use, even more years ago. Abnormal use makes it more perfect.

If you can't hide the memory allocation / redistribution operation, you can use the resource handle to minimize the possibility of memory leakage. Here is an example: I need to create an object in idle memory through a function and return it. At this time, I may forget to release this object. After all, we can't say that when this pointer is to be released, who will be responsible. Use the resource handle, here the auto_ptr in the standard library is used, which makes it clear to the place where you need to be.

#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 should be responsible for free this S?

}

Auto_ptr g () {

Return auto_ptr (new s); // Explicitly transmitted responsible to release this S

}

int main ()

{

COUT << "start main / n";

S * p = f ();

Cout << "after f () before g () / n";

// s * q = g (); // will be captured by the compiler

Auto_ptr q = g ();

COUT << "exit main / n";

// * P produces memory leaks

// * Q is released automatically

}

Consider resources in a more general sense, not just memory.

If you don't systematically apply these techniques in your environment (for example, the code you must use, or the other part of your program is simply original human (translation: the original is Neanderthals, Ni Ander, the old stone The time is widely distributed in the European people), so, wait, then pay attention to the use of a memory leak detector as part of the development process, or insert a garbage collection (GARBAGE Collector).

Why can't I continue after capturing an exception?

In other words, why does C do not provide a simple way, allowing the program to return to an exception throw out point and continue?

The main reason is that if you continue from the abnormal processing, then how the code after the throwing point is not presented, whether it is only continued, just like anything. Exception handlers cannot know, before continuing, whether the context environment (Context) is "correct". To make such a code correctly, throw an exception writer and capture the writer must be very familiar with each other's code with the context environment. This will produce very complex dependencies, so in any case, it will lead to a series of serious maintenance issues.

When I design C anomaly handling mechanism, I have seriously considered the possibility of allowing this continued, and this issue is discussed in detail in the process of standardization. See the exception handling chapter in the design and evolution of C languages.

In the discussion of a newsgroup, I have answered this problem with a slightly different way.

Why is there a function equivalent to a realloc () in C ?

If you need, you can of course use Realloc (). However, Realloc () is only guaranteed to work on such an array: they are allocated by Malloc (or similar), including some objects that do not have user-defined replication constructor (Copy Constructors). Moreover, to remember, in contrast to the usual expectations, realloc () sometimes must also copy its parameter array.

In C , a better way to process memory reassign is to use containers in the standard library, such as Vector, and make it self-growth.

How to use an exception?

See Chapter 4, Section 8.3, and Appendix E for C Programming Language. This appendix is ​​for how to write an unusually secure code in demanding programs, not for beginners. A key technique is "Resource Acquisiton IS Initialization), which uses some parsing capabilities to implement forced resource management. How to read a string from the input?

You can use this way to read a single word ended in space:

#include

#include

Using namespace std;

int main ()

{

COUT << "" please enter a word: / n ";

String S;

CIN >> S;

Cout << "you entered" << s << '/ n';

}

Note that there is no explicit memory management here, and it is not possible to result in a fixed-size buffer that overflows.

If you really want to get rid of a row, 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';

}

Chapter 3, "C Programming Language" (available online), you can find an introduction to a standard library tool such as a string and stream. For detailed comparisons using C and C , see my article "Learn Standard C as a New Language", you can book a list in your work (My Publications List) Download it in it.

Why does C does not provide "Finally" construct?

Because C provides another method, it is almost always better: "Resource Acquisiton IS Initialization" technology. Basic ideas are to express resources through a local object, so that the destructor of the local object will release resources. In this way, the programmer will not forget to release resources. for 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_HANDE (file * pp)

{P = PP; if (p == 0) throw open_error (errno);

~ File_handle () {fclose (p);

Operator file * () {returnit p;

// ...

}

Void f (const char * fn)

{

FILE_HANDLE F (FN, "RW"); // Open Fn to read and write

/ / Use File by f

}

In one system, you need to use a "resource handle" class for each resource. In any case, we don't need to write "Finally" statement for each resource. In real-time systems, resources are far more than the type of resource, so the "resource gain, initialization" technology produces less code than using the "Finally" constructor. What is an automatic pointer (Auto_PTR), why don't you automatically array (Auto_Array)?

Auto_ptr is an example of a very simple handle, defined in , supports exception security by "resource gain, initialization" technology. Auto_PTR holds a pointer to be used as the pointer, and release the pointed object at the end of the survival. Example:

#include

Using namespace std;

Struct x {

Int m;

// ..

}

Void f ()

{

Auto_ptr p (new x);

X * q = new x;

P-> m ; // use P like a pointer

Q-> m ;

// ...

Delete q;

}

If an exception is thrown in the section, the P-held object will be properly released by the destructor of Auto_PTR, while the X object to which the q generates memory leakage. For more details, see "C Programming Language" 14.4.2.

Auto_ptr is a very simple class. In particular, it is not a reference count (Reference Counted) pointer. If you assign an auto_ptr to another, the assigned auto_ptr will hold a pointer, and the original auto_ptr will hold 0. 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";

}

A pointer to 0 will be printed and a pointer to non-0. E.g:

P 0x0 q 0x378d0

Auto_ptr :: get () Returns the auxiliary pointer.

This "transfer" semantics is unlikely to "copy" semantics, which is surprising. In particular, you will never use Auto_Ptr as a member of a standard container. Standard containers require usually "replication" semantics. E.g:

Std :: Vector > v; // error

Auto_PTR holds only pointers pointing to a separate element rather than pointers to an array:

Void f (int N)

{

Auto_ptr p (new x [n]); // error

// ...

}

This is wrong, because the destructor will call DELETE instead of delete [] to release the pointer, so that the remaining N-1 X destructor is called.

So do we need an Auto_Array to hold an array? Do not. No auto_Array. The reason is that there is no such need at all. Better solution is to use Vector:

Void f (int N)

{

Vector v (n);

// ...

}

When the ... part is abnormal, the destructor of V is called correctly. Can I mix use C-style and C style memory distribution and redistribution?

In this sense: you can use malloc () and new in the same program.

In this sense: You can't use malloc () to create an object and release it through Delete. You can't build a new object with NEW, then release it by Free (), or build a new one in an array via Realloc ().

New and delete operations in C ensure the correct constructor and destructors: constructor and destructor are called when they need them. C Style Functions Alloc (), Calloc (), Free (), and Realloc () cannot guarantee this. In addition, use new and delete to get and release the original memory, not necessarily to ensure compatible with Malloc () and Free (). If this mixed style can be used in your system, you can only say that you walk - temporary.

If you think need to use Realloc () - or do more - Consider using the VECTOR in the standard library. E.g:

// Read the word from the input to a string vector

Vector Words;

String S;

While (CIN >> S && S! = ".") Words.push_back (s);

Vector will be automatically growing.

More examples and discussion, see my article "Learning Standard C As A New Language", you can download it in your book list (My Publications List).

Why do I have to use a shape to convert * void?

In C language, you can implicate * VoID to * T. This is unsafe. think about it:

#include

int main ()

{

CHAR i = 0;

Char j = 0;

Char * p = & i;

Void * Q = P;

INT * PP = q; / * is not safe, can be in C, C is not * /

Printf ("% D% D / N", I, J);

* PP = -1; / * Cover the memory from I * /

Printf ("% D% D / N", I, J);

}

Using a T-type T * will be a disaster. Therefore, in C , if you get a T * from a void *, you must perform explicit conversion. For example, to get the awkward effect of the above program, you can write this:

INT * PP = (int *) q;

Or use a new type of shape to make this type of conversion operation that does not check be clearer:

INT * PP = static_cast (q);

The shape is best to avoid.

In C language, one of the most common applications of unsafe conversion is to give Malloc () results to a suitable pointer. E.g:

INT * P = Malloc (sizeof (int));

In C , use the type of secure New operator:

INT * P = new int;

Incidentally, the New operator also provides a new feature of Malloc ():

NEW does not accidentally allocate the amount of memory;

New will implicitly check the internal deposits, and

NEW provides initialization. Example:

Typedef std :: complex cmplx;

/ * C style: * /

CMPLX * P = (CMPLX *) Malloc (intend); / * Error: Type is incorrect * /

/ * Forgot to test P == 0 * /

IF (* p == 7) {/ * ... * /} / * bad, forget the initialization * p * /

// C style:

CMPLX * Q = New CMPLX (1, ​​2); // If the memory is exhausted, a Bad_alloc exception will be thrown

IF (* q == 7) {/ * ... * /}

How do I define a constant of in-class?

If you need a constant defined by constant expressions, such as the range of arrays, you have two options:

Class x {

Static const INT C1 = 7;

Enum {c2 = 19};

CHAR V1 [C1];

CHAR V2 [C2];

// ...

}

At first glance, the declaration of C1 is clearer, but it should be noted that the constant must be an integer or enumeration type initialized by a constant expression, but must be Static and Const form. This is a very serious limit:

Class y {

Const Int C3 = 7; // Error: Not Static

Static Int C4 = 7; // Error: Not const

Static const float c5 = 7; // Error: Not integer

}

I tend to use enumeration methods because it is more convenient, and it will not induce me to use irregularized class initialization syntax.

So why does this inconvenient limit? In general, classes are declared in a header file, while header files are included in many units that are called. However, in order to avoid complex compiler rules, C requires only one separate definition of each object. This rule is destroyed if C is allowed to define an entity that occupy a memory as an object within a class. For the trade-offs in this design, see the design and evolution of C languages.

If you don't need to use a constant expression to initialize it, you can get greater flexibility:

Class z {

Static char * p; // initialized in the definition

const Int i; // Initialization in the constructor

PUBLIC:

Z (INT II): i (ii) {}

}

Char * z :: p = "Hello, There";

You can get the address of a Static member, and when it is only a class exterior:

Class ae {

// ...

PUBLIC:

Static const INT C6 = 7;

Static const INT C7 = 31;

}

Const Int ae :: C7; // Definition

INT f ()

{

Const int * p1 = & ae :: c6; // error: C6 does not have left value

Const Int * P2 = & ae :: C7; // OK

// ...

}

Why does Delete not set an operation?

think about it:

Delete P;

// ...

DELETE P; If there is no P, if there is no P, then the second "delete p;" will be a serious error because the implementation of C (the original is a C Implementation, when the implementation of VC The specific tool for C standards cannot effectively prevent this (unless the informal preventive means). Since Delete 0 is harmless from the definition, then a simple solution is that "Delete P;" is performed, and "p = 0;" will then be performed. However, C does not guarantee this.

One reason is that DELETE operands do not require a left value. think about it:

DELETE P 1;

Delete f (x);

Here, the executed delete does not have a pointer that can be given 0. These examples may be rare, but they do not point out, why not guarantee that "any pointer to the deleted object is 0" is not possible. A simple way to bypass this "rule" is that there are two pointers to point to the same object:

T * p = new t;

T * Q = P;

Delete P;

Delete q; // bad!

C Explicitly allows the Delete operation to set the left value of 0, and I have hoped that the implementation of C can do this, but this idea does not become popular in C implementation.

If you think that it is important to set up 0, consider using a destroyed function:

Template Inline Void Destroy (T * & P) {Delete P; P = 0;

Consider, this is why it is necessary to rely on a standard library container, handle, and the like to minimize the NEW and DELETE explicit calls to a minimum.

Note that the pointer (by allowing the pointer is enabled by allowing pointers), it is possible to prevent DESTROY () from being called on the right value (RVALUE):

INT * f ();

INT * P;

// ...

Destroy (f ()); // Error: You should use a very amount of non-const) to pass the right value

Destroy (p 1); // Error: You should use a very quoted reference to pass the right value

Can I write "void main ()"?

This definition:

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

Never permitted in C , the same in the C language. See ISO C Standard 3.6.1 [2] or ISO C standard 5.1.2.2.1. The implementation of the specification accepts this way:

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

with

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

A specification implementation may provide many versions of main (), but they must return INT types. Main () returned the int value, which is the way the program returns a value to the system that calls it. In systems that do not have this method, the return value is ignored, but this does not make "void main ()" becomes legitimate in C or C. Even if your compiler accepts "Void Main ()", you should also avoid using it, otherwise you will be regarded as ignorant of C and C programmers.

In C , Main () does not need to include an explicit returnite statement. In this case, the return value is 0, indicating success. For example: #include

int main ()

{

Std :: Cout << "This Program Returns The Integer Value 0 / N";

}

Note that whether ISO C is also C99, it is not allowed to leak the type in the statement. That is to say, with C89 and ARM C to form a control, when the type is missing, does not guarantee "int". then:

#include

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

It is wrong because the return type of main () is missing.

Why can't I overload symbols, ::, sizeof, and so on?

Most operators can be overloaded by programmers. The exception is:

(Point symbol) ::?: Sizeof

There is no fundamental reason to prohibit overloading? Just because, I didn't find any particular situation that needs to be overloaded a three-yuan operator. Note an overloaded expression 1? Expression 2: The function of expression 3 cannot be guaranteed to express only one of the expressions 3 will be executed.

SIZEOF is not able to be overloaded because of built-in operations, such as incrementing a pointer to an array, must rely on it. think about it:

X a [10];

X * p = & a [3];

X * Q = & a [3];

P ; // P point to a [4]

// then the integer value of P must be larger than the integer value of the q (X)

Therefore, SIZEOF (X) cannot give a different new meaning by programmers to avoid violation of basic syntax.

In n :: m, no matter N or M is not a value expression; n and m are the names known by the compiler, :: Perform a (compile period) range resolution instead of expression. You can imagine, if you reload x :: y, X may be an object rather than a name space (namespace) or a class, which will cause - Contrary to the original performance - generate new syntax (allowed Expression 1 :: Expression 2). Obviously, this complexity will not bring any benefits.

In theory,. (Point operand) can be overloaded by using and-> the same technique. However, this will lead to a problem, that is, it is not determined to be overloaded. Object, or an object of reference. E.g:

Class y {

PUBLIC:

Void f ();

// ...

}

Class X {/ / Assume you overload.

Y * p;

Y & operator. () {Return * p;}

Void f ();

// ...

}

Void G (X & X)

{

X.f (); // x :: f or y :: f or wrong?

}

This problem can be solved in several different ways. At normalization, it is best not to agree. For more details, see "Design and Evolution of C Language".

How to convert a integer value into a string?

The easiest way is to use a stringstream:

#include

#include #include

Using namespace std;

String Itos (int i) // converts 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";

}

Naturally, this technique can convert any type of type << output to a string. For more descriptions of string streams, see "C Programming Language" 21.5.3.

"INT * P" is correct or "int * p" correct?

Both are correct, because both are valid in C and C , and the meaning is exactly the same. For the definition of language and related compilers, we can also say "int * p" or "int * p".

The choice between "INT * P" and "int * p" is not related to the correct or wrong, and only the style and side focus. C side heavy expression; more about the declarations often take into account more. On the other hand, C attaches great importance to the type.

A "typical C programmer" writes "int * p", and explains "* p indicating what kind of INT" to emphasize grammar, and may indicate the syntax of C (with C ) to prove the correctness of this style. . Yes, in the syntax * is bound to the name P.

A "typical C programmer" writes "int * p", and explains "P is a pointer type pointing to int" to emphasize the type. Yes, P is a pointer type pointing to INT. I clearly tend to focus on this focus, and think it is important for learning more advanced C .

Severe chaos (only) occur when people try to declare several pointers in a statement:

INT * p, p1; // maybe it is wrong: P1 is not an int *

Put * on the side of the name, it seems that this error cannot be effectively reduced:

INT * P, P1; // maybe it is wrong?

Write a statement for each name to solve the problem to maximize the problem - especially when we initialize the variable. People almost do not write this:

INT * P = & I;

INT P1 = P; // Error: INT is initialized with an int *

If they really do it, the compiler will also point out.

Whenever things can be completed, some people will be confused. Whenever things are just a style, the debate will endless. Write a statement for each pointer, and always initialize the variable, so that the sources of chaos have disappeared. For more discussions about C language, see the design and evolution of C languages.

Which layout style is the best for my code?

This style is a personal hobby. People often hold strong opinions on the problem of layout style, but maybe consistency more important than a particular style. Like most people, I spent a long time to make a fixed conclusion for my preferences.

I personally use the style of "K & R". When using the constructor without the C language, it is necessary to add new habits, which becomes a style sometimes referred to as "stroustrup". E.g:

Class C: public b {

Public: // ...

}

Void f (int * p, int max)

{

IF (p) {

// ...

}

For (INT I = 0; i

// ...

}

}

Better than most layout style, this style retains vertical spaces, I like to align the screen as much as possible. The placement of the large brackets starting with the function helps me the first eye to define the definitions and functions of the class.

It is very important to indent.

Design problems, such as the use of abstract base classes as primary interfaces, using templates to express flexible types of safety abstraction, as well as correct use of abnormalities to express errors, more important than layout style.

Should I put "const" or after the type?

I put it in front, but that is just a personal hobby problem. "Const T" and "T Const" are always allowed, and it is equivalent. E.g:

Const int A = 1; // ok

INT const b = 2; // Also OK

I guess the first version may confuse the programmer of a minority (more sighfactory specification).

why? When I invent "Const" (the initial name is "Readonly", and there is a corresponding "Writeonly"), I will allow it to appear before or after the type, because this will not bring anything unclear. C and C prior to standards specify very little (if any) specific order specification.

I don't remember that there have been in-depth thinking or discussions in any relevant order issues. At that time, some of the early users - especially I - just like this:

Const Int C = 10;

It looks better than this:

INT const c = 10;

Maybe I have also been influenced: some of my earliest use "readonly"

Readonly INT C = 10;

More readability than this:

INT Readonly C = 10;

The earliest use "const" (C or C ) code I created, it seems that "Readonly" has been replaced worldwide.

I remember the choice of this grammar in a few people - such as Dennis Ritchie, I discussed it, but I don't remember which language I talked at the time.

Note that in the fixed pointer (const "," const "will always appear after" * ". E.g:

INT * const p1 = q; / / point to the fixed pointer to the INT variable

INT const * p2 = q; // Pointer to INT constant

Const int * p3 = q; // Pointer to INT constant

What is the problem with using the macro?

Macro does not follow the rules of scope and types in C . This often leads to some subtle or less subtle problems. Therefore, C provides more suitable C (translation: original to C , as a substitute for C except for part other than C ), such as inline functions, template and name space.

think about it:

#include "someheader.h"

Struct s {

Int alpha;

Int Beta;

}

If someone wrote a macro called "alpha" or "beta", it will not be compiled, or is incorrectly compiled, and unpredictable results. For example, "someHeader.h" may include: #define alpha 'a'

#define beta b [2]

The habit of all uppercase of the macro (and just macro) will help, but there is no language level of the language level. For example, although the name of the member is included in the interior of the structure, this does not help: the macro has been processed as a character stream before the compiler can correctly identify this. By the way, this is a major reason why C and C program development environment and tools can be simplified: people and compilers see different things.

Unfortunately, you can't assume that other programmers can always avoid this kind of thing that you think "quite idiot". For example, some people recently report me, they have encountered a macro containing goto. I have also seen this situation, and I have heard some - when it is very fragile - it is really a reasonable opinion. E.g:

#define prefix get_ready (); int RET__

#define return (i) RET __ = i; do_something (); goto exit

#define suffix exit: cleanup (); Return Ret__

Void f ()

{

Prefix;

// ...

Return (10);

// ...

Return (x );

// ...

SUFFIX;

}

As a maintenance programmer, this impression will be generated; "Hide" to a header file - this is not rare - making this "magic" more difficult to discern.

A common subtle problem is that a function-style macro does not comply with the rules passing through the function parameters. E.g:

#define Square (x) (x * x)

Void F (Double D, INT I)

{

Square (d); //

Square (i ); // bad: This means (i * i )

Square (D 1); // bad: This means (D 1 * D 1); that is, (D D 1)

// ...

}

"D 1" problem can be resolved by adding a pair of parentheses when "call" or macro definition:

#define Square (x) ((x) * (x)) / * This better * /

However, I has been executed twice (may not be interested in doing this), still exists.

Yes, I do know that some special macros do not cause the problem of C / C pretreatment macro. However, I am very unhappy to develop the macro in C . Alternatively, I recommend using the appropriate tools in the C language, such as inline functions, templates, and constructors (for initialization), and destructive functions (used to clear), exception (used to exit context environments), and so on.

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

New Post(0)