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
#include
Using namespace std;
Struct x {
Int m;
// ..
}
Void f ()
{
Auto_ptr
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
Auto_PTR
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
Auto_PTR holds only pointers pointing to a separate element rather than pointers to an array:
Void f (int N)
{
Auto_ptr
// ...
}
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
// ...
}
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
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
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
/ * 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, the second "delete p;" will be a serious error because the C is implemented (the original is a C standard for the implementation of C ). The specific tool) does not 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
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:
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. E.g:
#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. Consider: 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 contain: #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 (); returnization 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.