在不使用智能指针的情况下,如果 new 出来了一个指针,那么必须在程序里手动 delete 一下。
但是如果在 delete 之前程序就 return 了,那么就会导致资源泄露。
直到程序退出, *** 作系统才会释放这段内存。
但是如果程序一直没有退出,这段内存就会一直没有释放。
危害,如下:
#include
using namespace std;
int main() {
int* ptr = new int(0);
if (...) {
return -1;
}
delete ptr;
return 0;
}
代码量少,资源少可能体现不太出来。
但是 new 出来的东西太多了都没有及时释放就会很糟糕了。
把指针放到对象里,调用析构函数的时候释放指针指向的内存。
如下面代码所示:
#include
using namespace std;
template
class MySmartPtr {
public:
MySmartPtr(T* ptr):_ptr(ptr){}
~MySmartPtr() {
delete _ptr;
}
T& operator*() {
return *_ptr;
}
private:
T* _ptr;
};
int main() {
MySmartPtr ptr(new int);
if (...) {
return -1;
}
return 0;
}
在 main 函数里定义了 ptr 对象,把 new 出来的内存传给这个对象。
ptr 是在栈里的,当 ptr 离开了 main 函数的作用域,由于栈里的东西会被释放,即 ptr 会调用西沟函数,就会释放堆里的刚刚 new 出来的内存。
首先看一下下面的代码:
#include
using namespace std;
template
class MySmartPtr {
public:
MySmartPtr(T* ptr):_ptr(ptr){}
~MySmartPtr() {
delete _ptr;
}
T& operator*() {
return *_ptr;
}
private:
T* _ptr;
};
int main() {
MySmartPtr ptr(new int);
MySmartPtr ptr2(ptr);
return 0;
}
运行会报错,ptr2 析构后,ptr 和 ptr2 指向的内存已经被释放了。
就成了野指针,再在 ptr 析构的时候在释放一次就报错。
下面的代码可以正常输出。
#include
#include
using namespace std;
int main() {
auto_ptr ptr(new int(0));
cout << *ptr << endl;
return 0;
}
下面的代码报错:
#include
#include
using namespace std;
int main() {
auto_ptr ptr(new int);
auto_ptr ptr2(ptr);
cout << *ptr << endl;
return 0;
}
注意,是 cout << *ptr << endl; 这行报错,不是 auto_ptr
下面是 auto_ptr 的拷贝构造函数:
auto_ptr(auto_ptr& _Right) noexcept : _Myptr(_Right.release()) {}
/*
_Ty* release() noexcept {
_Ty* _Tmp = _Myptr;
_Myptr = nullptr;
return _Tmp;
}
*/
也就是说被拷贝的指针指向 nullptr 了,因此输出指针指向的值会报错。
这就是非计数的意思吧。
#include
#include
using namespace std;
int main() {
unique_ptr ptr(new int(0));
unique_ptr ptr2(ptr);
cout << *ptr << endl;
return 0;
}
上面的代码也会报错,和 auto_ptr 不同的是,在 unique_ptr
看看 unique_ptr 的拷贝构造和赋值构造:
unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;
直接不允许。
但是 uniqu_ptr 可以进行右值类型的拷贝构造:
#include
#include
using namespace std;
int main() {
unique_ptr ptr(new int(0));
unique_ptr ptr2(move(ptr));
cout << *ptr << endl;
return 0;
}
上面的代码会在 cout << *ptr << endl; 这行报错。
当指针指向内存的时候,引用计数++,否则--。
当引用计数 == 0 的时候,内存才算释放掉了。
#include
#include
using namespace std;
int main() {
shared_ptr ptr(new int(0));
shared_ptr ptr2(ptr);
cout << ptr.use_count() << endl;
return 0;
}
输出 2.
shared_ptr 在多个对象的成员互指的时候可能出现内存泄露。
这样把 A 和 B 放到智能指针里的话引用计数都是 2 了。
结果就是怎么都不会减到 0.
#include
#include
using namespace std;
class B;
class A {
public:
shared_ptr b;
};
class B {
public:
shared_ptr a;
};
int main() {
shared_ptr ptrA(new A());
shared_ptr ptrB(new B());
ptrA->b = ptrB;
ptrB->a = ptrA;
cout << ptrA.use_count() << endl;
cout << ptrB.use_count() << endl;
return 0;
}
输出:
2
2
解决办法,就是使用下面的 weak_ptr.
weak_ptr只在定义的时候使用 shared_ptr.
#include
#include
using namespace std;
class B;
class A {
public:
weak_ptr b;
};
class B {
public:
weak_ptr a;
};
int main() {
shared_ptr ptrA(new A());
shared_ptr ptrB(new B());
ptrA->b = ptrB;
ptrB->a = ptrA;
cout << ptrA.use_count() << endl;
cout << ptrB.use_count() << endl;
return 0;
}
输出:
1
1
总结,shared_ptr 一下,引用计数++,weak_ptr 一下,引用计数不++。
那有一个问题,shared_ptr 类型的对象引用计数为 0 了,但是被 weak_ptr 了一下。
weak_ptr 之后怎么发现这个这问题呢?使用 lock() 方法强转一下,再判断一下强转之后指针是不是为空是不是还能用。
shared_ptr 就像名字一样,很弱,要用的话得强转成shared_ptr.
案例1:
#include
#include
using namespace std;
class A {
public:
void f() {
cout << "A.f()" << endl;
}
};
int main() {
shared_ptr ptrA(new A());
weak_ptr ptrB(ptrA);
cout << ptrA.use_count() << endl;
cout << ptrB.use_count() << endl;
shared_ptr ptrC = ptrB.lock();
ptrC->f();
cout << ptrA.use_count() << endl;
cout << ptrB.use_count() << endl;
cout << ptrC.use_count() << endl;
return 0;
}
输出:
1
1
A.f()
2
2
2
案例2:
#include
#include
using namespace std;
class B;
class A {
public:
weak_ptr b;
void fA() {
cout << "fA " << b.use_count() << endl;
}
};
class B {
public:
weak_ptr a;
void fB() {
shared_ptr pa = a.lock();
if (pa) {
pa->fA();
cout << "fB " << pa.use_count() << endl;
}
}
};
int main() {
shared_ptr ptrA(new A());
shared_ptr ptrB(new B());
ptrA->b = ptrB;
ptrB->a = ptrA;
cout << ptrA.use_count() << endl;
cout << ptrB.use_count() << endl;
ptrB->fB();
return 0;
}
输出:
1
1
fA 1
fB 2
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)