注意观察下面的代码,如果new出来的对象忘记被delete释放,是会发生内存泄漏的。
#include <iostream>
using namespace std;
class Person{
private:
int age;
public:
// 构造函数
Person(){
cout << "Person()" << endl;
}
void print_info(){
cout << "person_info" << endl;
}
//析构函数
~Person(){
cout << "~Person()" << endl;
}
};
void test_func(){
Person *p = new Person();
p->print_info();
// delete p;
}
int main(){
test_func();
}
那么如何让使用指针不必有那么大的心智负担了?因为局部变量使用之后,程序会自动帮我调用其析构函数,因此我们要将指针变量的创建修改修改:
//引入sp,其意为SmartPoint
class sp{
private:
Person *p;
public:
sp(){
p = NULL;
cout << "sp()" << endl;
}
sp(Person *other){
this->p = other;
cout << "sp(Person *other)" << endl;
}
~sp(){
if (p)
{
delete p;
}
cout << "~sp()" << endl;
}
// 对 -> 操作符进行重载
Person* operator->(){
return p;
}
};
void test_func(){
sp s = new Person();
s->print_info();
}
输出如下,可以看到我们并没有主动delete,程序就避免了内存泄漏。
Person()
sp(Person *other)
person_info
~Person()
~sp()
接下来我们继续改进:
sp(Person *other){
this->p = other;
cout << "sp(Person *other)" << endl;
}
// const是不能少的,
sp(const sp &other){
this->p = other.p;
cout << " sp(sp *other)" << endl;
}
~sp(){
if (p)
{
delete p;
}
cout << "~sp()" << endl;
}
// 对 -> 操作符进行重载
Person* operator->(){
return p;
}
};
void test_func(sp& s1){
// 会调用拷贝构造函数:sp(const sp &other)
sp s = s1; // 这是初始化,而不是赋值 ,赋值是:sp s; s = s1; 赋值会调用operator= 操作符函数,初始化会调用拷贝构造函数
s->print_info();
}
int main(){
sp s = new Person();
for(int i=0;i<2;i++){
test_func(s);
}
}
输出如下,程序发生了崩溃,因为两次进行delete p;
Person()
sp(Person *other)
sp(const sp *other)
person_info
~Person()
~sp()
sp(const sp *other)
person_info
~Person()
没事,遇事不要慌,东西都在后备箱,看我们的破解大法
class Person{
private:
int age;
int count;
public:
// 构造函数
Person(){
cout << "Person()" << endl;
}
//增加引用计数
void incCount(){
this->count++;
}
//减少引用计数
void decCount(){
this->count--;
}
// 获取引用计数
int getCount(){
return this->count;
}
void print_info(){
cout << "person_info" << endl;
}
//析构函数
~Person(){
cout << "~Person()" << endl;
}
};
//引入sp,其意为SmartPoint
class sp{
private:
Person *p;
public:
sp(){
p = NULL;
cout << "sp()" << endl;
}
sp(Person *other){
this->p = other;
this->p->incCount();
cout << "sp(Person *other)" << endl;
}
// const是不能少的,
sp(const sp &other){
this->p = other.p;
this->p->incCount();
cout << " sp(const sp *other)" << endl;
}
~sp(){
if (p)
{
if (p->getCount()>1)
{ //存在多个引用
p->decCount();
}else{
delete p;
}
}
cout << "~sp()" << endl;
}
// 对 -> 操作符进行重载
Person* operator->(){
return p;
}
};
Person只new了一个,因此也只析构了一次,程序正确:
Person()
sp(Person *other)
sp(const sp *other)
person_info
~sp()
sp(const sp *other)
person_info
~sp()
~Person()
~sp()
程序似乎还不够优美,1. 引用计数写在Person里面的 2. 如果换一个类怎么办了?
-
引入RefBase基类用于计数
-
使用模板类实现任意对象的自定义只能指针
一切尽在不言中,上代码
class RefBase{
private:
int count; //构造对象时不初始化会得到一个无法想象的结果
public:
RefBase():count(0){
cout << "RefBase()" << endl;
}
void incCount(){
this->count++;
}
void decCount(){
this->count--;
}
int getCount(){
return this->count;
}
virtual ~RefBase(){
cout << "~RefBase" << endl;
}
};
class Person : public RefBase{
private:
int age;
public:
// 构造函数
Person(){
cout << "Person()" << endl;
}
void print_info(){
cout << "person_info" << endl;
}
//析构函数
~Person(){
cout << "~Person()" << endl;
}
};
//引入sp,其意为SmartPoint
template<typename T>
class sp{
private:
T *p;
public:
sp(){
p = NULL;
cout << "sp()" << endl;
}
sp(T *other){
this->p = other;
this->p->incCount();
cout << "sp(Person *other)" << endl;
}
// const是不能少的, 拷贝构造函数:sp s2 = s1; sp s3(s1); 这两种创建对象的方式都会调用拷贝构造函数; sp s4; s4 = s1; 这种写法只会调用 operator= 操作符函数
sp(const sp &other){
this->p = other.p; // 指针的直接拷贝
this->p->incCount();
cout << " sp(const sp *other)" << endl;
}
~sp(){
if (p)
{
cout << "value is :" << p->getCount() << endl;
if (p->getCount()>1)
{ //存在多个引用
p->decCount();
}else{
delete p;
}
}
cout << "~sp()" << endl;
}
// 对 -> 操作符进行重载,返回Person的指针
T* operator->(){
return p;
}
};
template<typename T>
void test_func(sp<T>& s1){
// 会调用拷贝构造函数:sp(const sp &other)
sp<T> s = s1; // 这是初始化,而不是赋值 ,赋值是:sp s; s = s1; 赋值会调用operator= 操作符函数,初始化会调用拷贝构造函数
s->print_info();
}
int main(){
//只new了一次,
sp<Person> s = new Person();
//test_func结束被释放两次???
for(int i=0;i<2;i++){
test_func(s);
}
}
输出
RefBase
Person()
sp(Person *other)
sp(const sp *other)
person_info
value is :2
~sp()
sp(const sp *other)
person_info
value is :2
~sp()
value is :1
~Person()
~RefBase
~sp()
那么到这里是否就结束了呢?No, 并没有,实际上这个代码是线程不安全的。Android源码中就有很好的例子,__sync_fetch_and_add就是确保线程安全。
inline LightRefBase() : mCount(0) { }
inline void incStrong(__attribute__((unused)) const void* id) const {
__sync_fetch_and_add(&mCount, 1);
}
inline void decStrong(__attribute__((unused)) const void* id) const {
if (__sync_fetch_and_sub(&mCount, 1) == 1) {
delete static_cast<const T*>(this);
}
}