Note: the coding tool is CLion+Cygwin64
catalogue
Smart pointer
Smart pointers can be used to automatically recycle objects created in new mode. You need to import the memory header file before using it.
shared_ptr
#include <iostream> using namespace std; #include <memory> class Test{ public: ~Test(){ cout << "Test Destructor" << endl; } }; int main(){ Test * test = new Test; shared_ptr<Test> ptr(test); return 0; }
Output:
Test Destructor
You can see that in the above code, the delete function is not called to recycle the object, and the destructor of the object is also called.
Reference counter
There will be an object reference counter inside the smart pointer, which records the number of times the object is referenced by the smart pointer.
#include <iostream> using namespace std; #include <memory> class Test{ public: ~Test(){ cout << "Test Destructor" << endl; } }; int main(){ Test * test = new Test; shared_ptr<Test> ptr(test); cout << "The reference count value is:" << ptr.use_count() << endl; shared_ptr<Test> ptr2 = ptr; cout << "The reference count value is:" << ptr.use_count() << endl; cout << "The reference count value is:" << ptr2.use_count() << endl; return 0; }
Output:
The reference count value is: 1 The reference count value is: 2 The reference count value is: 2 Test Destructor
Circular reference
shared_ptr type smart pointer may cause circular reference, so the destructor of the object cannot be called.
#include <iostream> using namespace std; #include <memory> class B; class A{ public: shared_ptr<B> ptr; ~A(){ cout << "A Destructor" << endl; } }; class B{ public: shared_ptr<A> ptr; ~B(){ cout << "B Destructor" << endl; } }; int main(){ A * a = new A; B * b = new B; shared_ptr<A> ptrA(a); shared_ptr<B> ptrB(b); a->ptr = ptrB; b->ptr = ptrA; cout << "A The reference count for is:" << ptrA.use_count() << endl; cout << "B The reference count for is:" << ptrB.use_count() << endl; return 0; }
Output:
A The reference count for is: 2 B The reference count for is: 2
You can see that the print information in the destructors of A and B is not output, so the destructors of A and B are not called. This is because both A and B are created through new. Although ptrA and ptrB are recycled after the main function is executed, and the reference counter is reduced by 1, the mutual references in A and B still exist, the reference count is not 0, and the delete function is not called in the smart pointer to release A and B.
· To solve the problem of circular references, you can use another smart pointer, weak_ptr.
weak_ptr
weak_ptr type smart pointer will not increase the reference count of the object.
#include <iostream> using namespace std; #include <memory> class B; class A{ public: weak_ptr<B> ptr; ~A(){ cout << "A Destructor" << endl; } }; class B{ public: weak_ptr<A> ptr; ~B(){ cout << "B Destructor" << endl; } }; int main(){ A * a = new A; B * b = new B; shared_ptr<A> ptrA(a); shared_ptr<B> ptrB(b); a->ptr = ptrB; b->ptr = ptrA; cout << "A The reference count for is:" << ptrA.use_count() << endl; cout << "B The reference count for is:" << ptrB.use_count() << endl; return 0; }
Output:
A The reference count for is: 1 B The reference count for is: 1 B Destructor A Destructor
unique_ptr
unique_ptr does not allow the assignment of type pointers of the same type.
unique_ptr has no reference count.
#include <iostream> using namespace std; #include <memory> class Test{ public: ~Test(){ cout << "Test Destructor" << endl; } }; int main(){ Test * test = new Test; unique_ptr<Test> ptr(test); // unique_ ptr<Test> ptr2 = ptr; // Compilation failed return 0; }
Output:
Test Destructor
Copywriting smart pointer
#include <iostream> using namespace std; template <typename T> class Ptr{ private: T * object; int * count; public: Ptr(){ object = NULL; count = new int(1); } Ptr(T * t){ object = t; count = new int(1); } ~Ptr(){ if(--(*count) == 0){ if(object) { delete object; } delete count; object = NULL; count = 0; } } // When a new smart pointer object is initialized with a smart pointer object, the copy constructor is called Ptr(const Ptr<T> & ptr){ cout << "Called Ptr Copy constructor for" << endl; ++(*(ptr.count)); // If the smart pointer references another object before, it needs to be released first if(--(*count) == 0){ if(object){ delete object; } delete count; } object = ptr.object; count = ptr.count; } // It is used when initializing an object with the default constructor and assigning another object to this object= // So overload the = operator Ptr & operator=(const Ptr & ptr){ cout << "Overload called=operator functions " << endl; ++(*(ptr.count)); // If the smart pointer references another object before, it needs to be released first if(--(*count) == 0){ if(object){ delete object; } delete count; } object = ptr.object; count = ptr.count; return *this; } int use_count(){ return *(this->count); } }; class Test{ public: ~Test(){ cout << "Called Test Destructor" << endl; } }; int main(){ Test * test = new Test(); Ptr<Test> ptr(test); cout << "ptr Reference count is:" << ptr.use_count() << endl; // Call copy constructor Ptr<Test> ptr2 = ptr; cout << endl << "ptr Reference count is:" << ptr.use_count() << endl; cout << "ptr2 Reference count is:" << ptr2.use_count() << endl; // Call overloaded = operator function Ptr<Test> ptr3; ptr3 = ptr; cout << endl << "ptr Reference count is:" << ptr.use_count() << endl; cout << "ptr2 Reference count is:" << ptr2.use_count() << endl; cout << "ptr3 Reference count is:" << ptr3.use_count() << endl; return 0; }
Output:
ptr Reference count is: 1 Called Ptr Copy constructor for ptr Reference count: 2 ptr2 Reference count: 2 Overload called=operator functions ptr Reference count: 3 ptr2 Reference count: 3 ptr3 Reference count: 3 Called Test Destructor
Type conversion
const_cast
Constant conversion, you can convert a constant pointer to a normal pointer, so you can modify the value of the memory address pointed to by the pointer.
#include <iostream> using namespace std; class Test{ public: string name = "default"; }; int main(){ const Test* test = new Test; cout << "before: name = " << test->name << endl; // test->name = "update";// Compilation failed Test * ntest = const_cast<Test*>(test); ntest->name = "update"; cout << "after: name = " << test->name << endl; if(test){ delete test; test = NULL; } return 0; }
Output:
before: name = default after: name = update
static_cast
Static conversion is mainly to convert pointer types. For example, you can convert void * to int *, double * and so on, which is used in the previous thread.
Static conversion can also convert objects of a parent type to subtypes.
The static conversion calling function looks at the variable type on the left of the equal sign, which is determined by the compiler.
#include <iostream> using namespace std; class Base{ public: void show(){ cout << "Base show" << endl; } }; class Sub: public Base{ public: void show(){ cout << "Sub show" << endl; } }; int main(){ int number = 9999; void * vp = &number; int * ip = static_cast<int*>(vp); cout << "*ip = " << *ip << endl; Base * base = new Base; // Static conversion can also convert objects of a parent type to subtypes. Sub * sub = static_cast<Sub*>(base); base->show(); // The static conversion calling function looks at the variable type on the left of the equal sign, which is determined by the compiler. sub->show(); if(base){ // delete whoever comes out of new delete base; base = NULL; } return 0; }
Output:
*ip = 9999 Base show Sub show
dynamic_cast
The dynamic transformation runtime knows which function to call.
Dynamic conversion cannot convert a parent type object to a child type object.
If the dynamic conversion returns NULL, the conversion fails.
In dynamic conversion, the function of the parent class must be declared as a virtual function.
#include <iostream> using namespace std; class Base{ public: virtual void show(){ cout << "Base show" << endl; } }; class Sub: public Base{ public: void show(){ cout << "Sub show" << endl; } }; int main(){ Base * base = new Base; // Dynamic conversion cannot convert an object of a parent type to a child type. Sub * sub = dynamic_cast<Sub*>(base); if(sub){ cout << "Base->Sub Conversion succeeded" << endl; sub->show(); }else{ cout << "Base->Sub switch views" << endl; } Sub * sub2 = new Sub; Base * base2 = dynamic_cast<Base*>(sub2); if(base2) { cout << "Sub->Base Conversion succeeded" << endl; base2->show(); }else{ cout << "Sub->Base switch views" << endl; } if(base){ // delete whoever comes out of new delete base; base = NULL; } if(sub2){ // delete whoever comes out of new delete base; base = NULL; } return 0; }
Output:
Base->Sub switch views Sub->Base Conversion succeeded Sub show
reinterpret_cast
It has the functions of coercion and static conversion. At the same time, it can also convert objects into values and values into objects.
It can be used to transfer object addresses between Java and C + +. Reinterpret is used in both Handler and Binder in Android source code_ Cast, C + + converts the object into a long value and passes it to Java. When C + + needs the object, Java passes the long value to C + +.
#include <iostream> using namespace std; class Test{ public: void show(){ cout << "test reinterpret_cast" << endl; } }; int main(){ Test * test = new Test; long paddr = reinterpret_cast<long>(test); cout << "paddr = " << paddr << endl; Test * reTest = reinterpret_cast<Test*>(paddr); reTest->show(); printf("paddr = %p\n", paddr); printf("test The stored address values are:%p\n", test); printf("reTest The stored address values are:%p\n", reTest); if(test){ delete test; test = NULL; } return 0; }
Output:
paddr = 34360045024 test reinterpret_cast paddr = 0x80004ade0 test The stored address value is: 0 x80004ade0 reTest The stored address value is: 0 x80004ade0