注:编码工具是CLion+Cygwin64
目录
智能指针
引用计数器
循环引用
weak_ptr
unique_ptr
仿写智能指针
类型转换
const_cast
static_cast
dynamic_cast
reinterpret_cast
智能指针
智能指针可以用于自动回收new方式创建的对象。使用前需要先导入memory头文件。
shared_ptr#includeusing namespace std; #include class Test{ public: ~Test(){ cout << "Test析构函数" << endl; } }; int main(){ Test * test = new Test; shared_ptr ptr(test); return 0; }
输出:
Test析构函数
可以看到上面代码中没有调用delete函数回收对象,对象的析构函数也被调用了。
引用计数器智能指针内部会有一个对象引用计数器,记录对象被智能指针引用的次数。
#includeusing namespace std; #include class Test{ public: ~Test(){ cout << "Test析构函数" << endl; } }; int main(){ Test * test = new Test; shared_ptr ptr(test); cout << "引用计数值为:" << ptr.use_count() << endl; shared_ptr ptr2 = ptr; cout << "引用计数值为:" << ptr.use_count() << endl; cout << "引用计数值为:" << ptr2.use_count() << endl; return 0; }
输出:
引用计数值为:1 引用计数值为:2 引用计数值为:2 Test析构函数循环引用
shared_ptr类型的智能指针可能导致循环引用,因而对象的析构函数无法被调用。
#includeusing namespace std; #include class B; class A{ public: shared_ptr ptr; ~A(){ cout << "A析构函数" << endl; } }; class B{ public: shared_ptr ptr; ~B(){ cout << "B析构函数" << endl; } }; int main(){ A * a = new A; B * b = new B; shared_ptr ptrA(a); shared_ptr ptrB(b); a->ptr = ptrB; b->ptr = ptrA; cout << "A的引用计数为:" << ptrA.use_count() << endl; cout << "B的引用计数为:" << ptrB.use_count() << endl; return 0; }
输出:
A的引用计数为:2 B的引用计数为:2
可以看到并没有输出A和B的析构函数中的打印信息,所以A和B的析构函数没有被调用。这是因为A和B都是通过new方式创建的对象,虽然ptrA和ptrB在main函数执行完被回收了,引用计数器也都减了1,但是A和B中的相互引用还存在,引用计数不为0,智能指针中没有调用delete函数释放A和B。
·要解决循环引用的问题,可以用另一种智能指针weak_ptr。
weak_ptrweak_ptr类型的智能指针,不会增加对象的引用计数。
#includeusing namespace std; #include class B; class A{ public: weak_ptr ptr; ~A(){ cout << "A析构函数" << endl; } }; class B{ public: weak_ptr ptr; ~B(){ cout << "B析构函数" << endl; } }; int main(){ A * a = new A; B * b = new B; shared_ptr ptrA(a); shared_ptr ptrB(b); a->ptr = ptrB; b->ptr = ptrA; cout << "A的引用计数为:" << ptrA.use_count() << endl; cout << "B的引用计数为:" << ptrB.use_count() << endl; return 0; }
输出:
A的引用计数为:1 B的引用计数为:1 B析构函数 A析构函数unique_ptr
unique_ptr不允许同类型的类型指针赋值。
unique_ptr没有引用计数。
#includeusing namespace std; #include class Test{ public: ~Test(){ cout << "Test析构函数" << endl; } }; int main(){ Test * test = new Test; unique_ptr ptr(test); // unique_ptr ptr2 = ptr; // 编译不通过 return 0; }
输出:
Test析构函数仿写智能指针
#includeusing namespace std; template 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; } } // 用一个智能指针对象初始化一个新的智能指针对象时,会调用拷贝构造函数 Ptr(const Ptr & ptr){ cout << "调用了Ptr的拷贝构造函数" << endl; ++(*(ptr.count)); // 如果智能指针之前引用其他对象,则需要先释放 if(--(*count) == 0){ if(object){ delete object; } delete count; } object = ptr.object; count = ptr.count; } // 先用默认构造函数初始化一个对象,再将另一个对象赋值给此对象时,会用到= // 所以要重载=运算符 Ptr & operator=(const Ptr & ptr){ cout << "调用了重载=运算符函数" << endl; ++(*(ptr.count)); // 如果智能指针之前引用其他对象,则需要先释放 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 << "调用了Test析构函数" << endl; } }; int main(){ Test * test = new Test(); Ptr ptr(test); cout << "ptr引用计数为:" << ptr.use_count() << endl; // 调用拷贝构造函数 Ptr ptr2 = ptr; cout << endl << "ptr引用计数为:" << ptr.use_count() << endl; cout << "ptr2引用计数为:" << ptr2.use_count() << endl; // 调用重载=运算符函数 Ptr ptr3; ptr3 = ptr; cout << endl << "ptr引用计数为:" << ptr.use_count() << endl; cout << "ptr2引用计数为:" << ptr2.use_count() << endl; cout << "ptr3引用计数为:" << ptr3.use_count() << endl; return 0; }
输出:
ptr引用计数为:1 调用了Ptr的拷贝构造函数 ptr引用计数为:2 ptr2引用计数为:2 调用了重载=运算符函数 ptr引用计数为:3 ptr2引用计数为:3 ptr3引用计数为:3 调用了Test析构函数类型转换 const_cast
常量转换,可以将常量指针转换为普通指针,从而可以修改该指针所指向的内存地址的值。
#includeusing namespace std; class Test{ public: string name = "default"; }; int main(){ const Test* test = new Test; cout << "before:name = " << test->name << endl; // test->name = "update";// 编译不通过 Test * ntest = const_cast (test); ntest->name = "update"; cout << "after:name = " << test->name << endl; if(test){ delete test; test = NULL; } return 0; }
输出:
before:name = default after:name = updatestatic_cast
静态转换主要是转换指针类型,例如可以将void*转换为int*,double*等,前面线程中就用到了。
静态转换还可以将父类型的对象转换为子类型。
静态转换调用函数看等号左边变量类型,这是在编译器就决定了的。
#includeusing 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 (vp); cout << "*ip = " << *ip << endl; base * base = new base; // 静态转换还可以将父类型的对象转换为子类型。 Sub * sub = static_cast(base); base->show(); // 静态转换调用函数看等号左边变量类型,这是在编译器就决定了的。 sub->show(); if(base){ // 谁是new出来的,就delete谁 delete base; base = NULL; } return 0; }
输出:
*ip = 9999 base show Sub showdynamic_cast
动态转换运行期才能知道调用哪个函数。
动态转换不能将父类型对象转换为子类型对象。
动态转换返回如果是NULL,表示转换失败。
动态转换中,父类的函数必须声明为虚函数。
#includeusing 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; // 动态转换不可以将父类型的对象转换为子类型。 Sub * sub = dynamic_cast(base); if(sub){ cout << "base->Sub转换成功" << endl; sub->show(); }else{ cout << "base->Sub转换失败" << endl; } Sub * sub2 = new Sub; base * base2 = dynamic_cast (sub2); if(base2) { cout << "Sub->base转换成功" << endl; base2->show(); }else{ cout << "Sub->base转换失败" << endl; } if(base){ // 谁是new出来的,就delete谁 delete base; base = NULL; } if(sub2){ // 谁是new出来的,就delete谁 delete base; base = NULL; } return 0; }
输出:
base->Sub转换失败 Sub->base转换成功 Sub showreinterpret_cast
强制转换,静态转换有的功能它都有,同时它还可以将对象转换为数值,可以将数值转换为对象。
可以用于在Java和C++之间传递对象地址,Android源码中Handler和Binder都用到了reinterpret_cast,C++将对象转换为long型值传给Java,C++在需要对象的时候,Java再将long型值传给C++。
#includeusing namespace std; class Test{ public: void show(){ cout << "test reinterpret_cast" << endl; } }; int main(){ Test * test = new Test; long paddr = reinterpret_cast (test); cout << "paddr = " << paddr << endl; Test * reTest = reinterpret_cast (paddr); reTest->show(); printf("paddr = %pn", paddr); printf("test存储的地址值是:%pn", test); printf("reTest存储的地址值是:%pn", reTest); if(test){ delete test; test = NULL; } return 0; }
输出:
paddr = 34360045024 test reinterpret_cast paddr = 0x80004ade0 test存储的地址值是:0x80004ade0 reTest存储的地址值是:0x80004ade0
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)