"C + + programming principles and Practice" reading notes (iv)

Source: Internet
Author: User

The default is to use vectors. --alex Stepanov



The most useful vector in the C + + standard library is vectors. A vector provides a series of elements of a specified type. You can find an element by its index (subscript), use push_back () to extend the vector, use size () to get the number of elements in a vector, and prevent access to the out-of-bounds vector elements. The standard library vector is a convenient, flexible, efficient (time and space), static, type-safe element container.


Basic knowledge of vectors


We started with the gradual design of vectors, considering a very simple use.

Vector<double> age (4); age[0] = 0.23;age[1] = 22.0;age[2] = 27.2;age[3] = 54.2;

Obviously, we create a vector with four elements of type double and assign values of 0.33,22.0,27.2 and 54.2 to these four elements respectively. The four elements are numbered 0, 1, 2, and 3. The element number in the C + + standard reservoir can only start at 0. Numbering from 0 is very common, and it is a common convention among C + + programmers. The number of elements in a vector is called its size. Therefore, age has a size of 4. The elements in a vector are numbered (indexed) to 0 to Size-1. For example, an element in age is numbered 0 to Age.size ()-1. In view of this, we can define our own first version of the vector class:

class vector{int sz;double * elem;public:vector (int s); int size () const {return sz;};}


operator sizeof


So, how much memory does an int actually occupy? A pointer? The operator sizeof will answer these questions:

cout << "The size of char is" << sizeof (char) << ' << sizeof (' a ') << "\ n"; cout << " The size of int is "<< sizeof (int) <<" << size (0;cout) << ' \ n '; int * p = siz << "the E of int * is "<< sizeof (int *) <<" "<< sizeof (p) << ' \ n ';

As you can see, we could use sizeof for a type name or expression. For a type name, sizeof gives the size of this type of object, and for an expression, sizeof gives the size of the result of the expression. The result of sizeof is a positive integer, and the size of the sizeof (char) cell is defined as 1. In the case, a char is saved in one byte, so sizeof reports the number of bytes consumed. Each C + + implementation does not guarantee that one type is the same size.


Free space allocation


We require the use of the new operator to allocate memory from the free space:

(1) The new operator returns a pointer to the allocated memory.

(2) A pointer to a specific type of object.

(3) A pointer does not know how many elements it points to.

(4) The new operator can allocate memory for a single element or a series of (array) elements.

The new operator can allocate memory for a single element or a series of (array) elements. The number of objects allocated may be variable. This is important because it allows us to choose how many objects to allocate at run time.

This is the "practical explanation". The theoretical explanation is that "allowing different types to be assigned to pointers will result in type errors."


We'll always make sure to assign a value to the object before it is used, that is, we want to confirm that the pointer is initialized and the object that the pointer is pointing to is initialized. Consider the following code:

Double * P0;double * p1 = new double;double * P2 = new double (5.5);d ouble * p3 = new DOUBLE[5];


Null pointer


If you do not have another pointer to initialize a pointer, use 0:

Double * P0 = 0;

When 0 is assigned to a pointer, 0 is called a null pointer. We often determine whether a pointer is valid (as if it points to something) by detecting whether the pointer is 0.


Free Space Release


The new operator allocates memory from the free space. Because the memory of a computer is limited, it is usually a good idea to release memory back to free space after the use is over. This allows the free space to re-use the memory for new allocations. For large programs and long-running programs, the reuse of this free space is important. For example:

Double * CALC (int res_size, int max) {double * p = new Double[max];   Double * res = new Double[res_size]; return res;}


Double * R = Calc (100,1000);

In a write operation, each call to Calc () causes the double array assigned to p to "leak". The operator that returns memory to free space is called delete. We use delete for a pointer to return the memory allocated by new so that the memory can be used for future allocations. Now, this example becomes:

Double * CALC (int res_size, int max) {double * p = new Double[max];d ouble * res = new Double[res_size];d elete [] P;retu RN Res;} Double * R = Calc (100,1000);d elete[] r;

Incidentally, this example proves one of the main reasons for using free memory: We can create objects in a function and pass them to the caller of the function. Here are two types of delete:

(1) Delete p releases the memory assigned to a single object by new.

(2) delete[] p releases the memory assigned to the array object by new.

It is tedious work for programmers to use the right way. Deleting an object two times is a bad mistake.

int * p = new int (5);d elete P;delete p;

The second delete p brings two questions:

(1) Therefore, the free space manager may change its internal data structure so that delete p cannot be executed correctly again, and you no longer own the object to which the pointer points.

(2) Free space management may have "reclaimed" p-pointed memory, so p may now point to other objects, and deleting other objects (owned by other parts of the program) will cause errors in your program.

Both of these problems occur in actual procedures; they are not only theoretically possible.

Deleting a null pointer does nothing (because a null pointer does not point to an object), so removing the null pointer is harmless.

int * p = 0;

Delete p;

Delete p;

Why are we all troubled by free memory? Does the compiler not indicate when we don't need a piece of memory and recycle it without human intervention? It can. This process is called automatic garbage collection or garbage collection. Unfortunately, automatic garbage collection is not free and is not ideal for all applications. If you actually need automatic garbage collection, you can insert an automatic garbage collector into your C + + program. A good garbage collector is effective.


When is it important to not leak memory? A program that runs "forever" cannot tolerate leaking memory. An operating system that cannot have a memory leak is an example of an "ever-running" program, as is the case with most embedded systems. Many programs use libraries as part of the system, so libraries are not subject to memory leaks. In general, it is a good idea that all programs do not produce a memory leak. Many programs will cause the leak to be attributed to sloppy, but that does not hit the point. When you run a program on an operating system, all memory is automatically returned to the system at the end of the program. This poses a problem if you know that your program will not use more memory than is available, and you may "reasonably" decide to leak memory straight to the operating system to free up memory for you. However, if you decide to do this, make sure that your estimated memory consumption is correct, otherwise people will have good reason to think that you are hasty.


Destructors


Constructors are implicitly called when the class of an object is created, and the destructor is implicitly called when an object leaves the scope. The constructor is used to confirm that an object is created and initialized correctly. In contrast, destructors are used to confirm that an object is destroyed correctly.

class vector{int sz; Double * Elem;public:vector (int s): SZ (s), Elem (new, Double[s]) {for (int i = 0; i < s; ++i) elem[i] = 0;} ~vector () {delete [] elem;}}


With this definition, we can use vectors like this:

void F3 (int n) {int * p = new int [n];    Vector v (n); delete [] p;}


Delete[] looks rather cumbersome and prone to error. For vectors, we do not have to use new to allocate memory, and to use delete[] at the end of a function to free memory. Vector has done the work and done it better. In particular, the vector cannot forget to tune its destructor to release the memory used by the element. It is important to process resources, which require the use of pre-application and release buffer space after use. The work is done by their destructors, and each class that owns the resource needs a destructor. If a member of a class has a destructor, the destructor is called when the object containing the member is destroyed.

struct customer{string name;vector<string> addresses;//...}; void Some_fct () {Customer fred;}


The destructors for members and base classes are called implicitly from the destructor of the derived class, regardless of user-defined or compiler-generated functions. Basically, all the rules can be summed up as: "When the object is destroyed, the destructor is called" (when it leaves the scope when the delete is invoked, etc.).


Destructors are conceptually simple, but they are the basis of most effective C + + compilation techniques. Its basic idea is simple:

(1) regardless of which resource a class object needs to use, this resource is obtained in the constructor.

(2) in the life of an object, it can release resources and acquire new resources.

(3) After the end of the object's lifetime, the destructor frees all resources owned by the object.

As a rule of thumb: If you have a class with virtual function functionality, it requires a virtual destructor. The specific reasons are:

1) If a class has virtual function functionality, it is often used as a base class.

2) If it is a base class, its derived classes are often allocated using new.

3) If a derived class object is allocated using new and is controlled by a pointer to its base class, it is often removed by a pointer to its base class.

Notice that the destructor is invoked implicitly or indirectly through delete. They are not called directly. This will save you a lot of troublesome work.


Accessing vector elements


To make the vectors available, we need a way to read and write elements. For starters, we can provide simple get () and set () member functions:

Class vector{Private:int sz;   Double * ELEM;   Public:vector (int s): SZ (s), Elem (new Double[s]) {} ~vector () {delete [] elem;}      int size () Const{return sz;}   Double get (int n) {return elem[n];} void set (int n, double v) {Elem[n] = V;}};


Both get () and set () can access the element, using the [] operator on the elem pointer: elem[n].

Now, we can build a double vector and use it:

Vector v (5); for (int i = 0; i < v.size (); ++i) {V.set (I, 1.1*i); cout << "v[" << I << "]==" << V.get (i) << ' \ n ';}

This will output:

V[0] = = 0v[1] = = 1.1v[2] = = 2.2v[3] = = 3.3v[4] = = 4.4

This is still a fairly simple vector, and the code that uses get () and set () is hard to see relative to the usual subscript notation. However, our goal is to start with a small and simple program, and to step through this way to test and augment our program. This development and repeated testing strategies can reduce errors and debug processes.

Vector *f (int s) {vector * p = new vector (s); return p;} void ff () {vector * q = f (4); Delete p;}


Notice that when we delete a vector, its destructor is called.

Vector * p = new vector (s);d elete p;

To create a vector in free space, the new operator:

First allocate memory for a vector.

The constructor of the vector is then activated to initialize the vector, and the constructor allocates memory for the elements of the vector and initializes the elements.


Delete vector, delete operator:

First, the destructor of the vector is activated; The constructor activates the destructors for these elements (if they have destructors), and then releases the memory used by those elements.

Then, release the memory used by the vector.


Type blending: untyped pointers and pointer type conversions

We are very close to the hardware level when using pointers to free-space allocation arrays. Basically, we map the operation of pointers (initialization, assignment, * and []) directly to machine instructions. At this level, the language provides only a little description of the convenience, as well as the compile-time consistency provided by the type system. Occasionally, we must not abandon this last point of protection.

Generally, we do not want to work without the protection of a type system, but sometimes there is no other option. We sometimes encounter situations where we need to face old code that is not designed according to the security type. In this case, we need two things:

1) A pointer to memory that does not know what kind of object you are saving.

2) An operation that, for such a pointer, tells the compiler pointer what type of operation (unconfirmed) it is pointing to.

The meaning of type void * is "point to the memory of the compiler that does not know the type." void * Can be used when we want to transfer an address between two pieces of code and do not know the actual type of each piece of code.


"C + + programming principles and Practice" reading notes (iv)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.