First, the problem introduced
About the new and delete operators in C + +,
We know that these two operators must exist in pairs to avoid memory leaks.
This is considered common sense when learning, however, it is often difficult to actually write code in the process.
Here are 3 things:
1, the code is very long.
It is easy to forget delete when you need to use the delete to be very far away from the new operator that corresponds to it. Of course, this situation can be completely avoided.
2, such as the following code:
void Test () {int *pi = new int (1); if (1) {return;} Delete pi; The program did not perform to this step}int main () {void Test (); return 0;}
Here we are in the test function, open up a length of 1 int type size of dynamic memory,
But then, into the IF statement, directly return, so the previously opened memory is not recycled, resulting in memory leaks.
In this case, we can modify this:
void Test () {int *pi = new int (1); if (1) {delete Pi;return;} Delete pi; The program did not perform to this step}int main () {void Test (); return 0;}
3, let the situation more complicated, look at this code:
void DoSomething () {if (1) {throw 1;}} void Test () {int *pi = new int (1);D osomething ();d elete Pi;} int main () {Try{test1 ();} catch (...) {;} return 0;}
In the test () function, it appears that new and delete are paired, and there is only one line of code in the middle.
However, when the middle of this dosomething () throws an exception that causes the program structure to not proceed in a methodical manner,
The delete pi is still not executed.
In this case, we can still make the following corrections:
void DoSomething () {if (1) {throw 1;}} void Test () {int *pi = new int (1); try{dosomething ();} catch (...) {Delete Pi;throw;} Delete pi;} int main () {Try{test1 ();} catch (...) {;} return 0;}
Add a try Catch statement to test as the dosomething () function throws an exception to the "mediation" that handles dynamic memory.
The above 3 cases are completely solved.
But especially when the program is similar to the structure of scenario 3, and even more complex, we have to add a lot of code, but only to deal with dynamic memory recycling.
This is very influential in development efficiency, and the program becomes difficult to read.
Second, a simple smart pointer
We know that in the member functions of a class, the existence of destructors seems to solve the problem of dynamic memory reclamation: When the program is scoped to the class, the destructor for that class is automatically called.
With this thought, we define a class template called Autoptr.
Template<typename t>class autoptr{public:autoptr (T *ptr = NULL): _ptr (PTR) {}autoptr (autoptr<t> &ap): _ PTR (ap._ptr) {ap._ptr = NULL;} ~autoptr () {if (_ptr! = null) {cout << "delete:" << _ptr << endl;delete _ptr;_ptr = NULL;}} Autoptr<t> operator= (autoptr<t> &ap) {if (This! = &ap) {if (_ptr! = ap._ptr) {delete _ptr;} _ptr = Ap._ptr;ap._ptr = NULL;} return *this;} T &operator* () {return *_ptr;} t* operator-> () {return _ptr;} protected:t* _ptr;
It is important to note that when the template type of this template class is struct or class, we need to access the members of that class, so we need a
Overloading of the operator, for example:
The known structure Stru is defined as follows:
struct Stru{void printtest () {std::cout << "hi" << Std::endl;}};
For such a class, when we execute the following code
Stru St1;stru *ps1 = &st1; Autoptr<stru> Aps1 (PS1); aps1->printtest ();//overloading of operators
The last line is overloaded with the "--" operator, but according to the previous definition of the "-and" operator, access to the
_PS1; This line of code is actually overloaded and should be: _ps1printtest ()
In fact, this is the compiler in order to ensure the readability of the code, the _ps1printtest optimization to _ps1->printtest ().
(This optimization is present in both g++ and VS2015.)
Of course, this smart pointer is not perfect, look closely at my copy constructor and assignment operator overload, you will find that whenever we copy or assign value, always let the source smart pointer empty, that is, the same time a period of dynamic memory can only be maintained by a smart pointer.
There is a reason for this: if there are a number of smart pointers pointing to the same dynamic space, then at the time of the destruction, the space will be reconstructed several times, the program must collapse. So I can only let this class have "dynamic memory at the same time can only be maintained by a smart pointer" feature.
In short, although imperfect, we still implement the class as a simple smart pointer autoptr. With it, we can do all the work of releasing the memory with the destructor without having to delete it manually.
The above-mentioned "imperfect" is mainly reflected in the following points, but also my next problem to solve:
1, "The same time a period of dynamic memory can only be maintained by a smart pointer" feature: This feature makes it and the normal pointer "not very similar", does not conform to the common programming habit,
In addition, if you manually construct more than one smart pointer to the same memory, still cause the destructor of the same memory when the destruction of multiple times, the program will still crash, so this approach does not fundamentally solve the problem;
2, smart pointer can only point to dynamic memory, if pointing to static memory, in the process of destruction will inevitably crash.
Third, improve
The autoptr I wrote earlier had a variety of imperfections that needed to be improved, due to the unreasonable copying and assignment
At the same time in a particular section of dynamic memory, there can only be one autoptr maintenance, for this problem, scopedptr and sharedptr are autoptr improvements
The Scopedptr class template is defined as follows:
Template<typename t>class scopedptr{public:scopedptr (t* ptr = NULL): _ptr (PTR) {}~scopedptr () {if (_ptr) {cout < < "Delete:" << _ptr << endl;delete _ptr;_ptr = NULL;}} T &operator* () {return *_ptr;} t* operator-> () {return _ptr;} Protected:scopedptr (scopedptr<t> &sp); Scopedptr<t> &operator= (scopedptr<t> &sp);p rotected:t* _ptr;};
The difference between this and autoptr is that
1: Copy constructor and assignment operator overloaded function declared only, not defined
2: And the declaration of the above two functions is placed in the protected limit characters
In this way, when we want to copy the construction or assignment of a variable of the SCOPEDPTR type, the program cannot be compiled because it does not define the corresponding function, so it prevents the occurrence of the assignment behavior.
In addition, it makes sense to place these two functions on the protected characters: if we declare that these two functions are public, then we can do the same to prevent the effect of the call.
But once this is done, the consequence is that when someone reads such a code, it may be misunderstood that we didn't have time to define the two functions, and then add the corresponding definitions to the "lily", so that the Scopedprt class is no different than the Autoptr class we defined earlier.
In addition, the malicious "troublemakers" once see such a loophole, it will be easy to destroy your program-just need to write the two functions to define it.
In summary, it makes sense to declare these two functions as protected, which prevents others from destroying the program.
adjourned
Smart pointers for C + + (3.30)