智能指针
在C++指针向来是最令人头疼的事情,我们来看一个栗子。
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
public:
Person() {
cout <<"Pserson()"<<endl;
}
~Person(){
cout << "~Person()"<<endl;
}
void printInfo(void){
cout<<"just a test function"<<endl;
}
};
void test_func(void)
{
Person *p = new Person();
p->printInfo();
}
int main(int argc, char **argv)
{
int i;
for (i = 0; i < 2; i++)
test_func();
return 0;
}
我们编译执行代码,输出如下结果:
可以看到代码执行了两次 Person() 构造函数,但是没有执行析构函数 ~Person() 函数对内存进行释放,这是非常危险的,代码会一直占用内存,这里才执行两次,如果执行成千上万次甚至可能导致代码崩溃,这时候需要用 delete 手动释放内存。我们对代码进行优化,修改 test_func(void) 函数:
void test_func(void)
{
//Person *p = new Person();
//p->printInfo();
//delete p;
Person per;
per.printInfo();
}
我们将Person p替换成了局部变量Person per或者用delete p手动释放内存。编译执行,看一下效果:
可以看到test_func(void)函数执行结束之后会自动调用析构函数,解决了内存泄漏的问题,但如果团队合作是我们也不能阻止别人使用指针呀。
我们进一步对代码进行优,我们定义一个SP(SmartPointer) 类:
class sp {
private:
Person *p;
public:
sp() : p(0) {}
sp(Person *other){
cout<<"sp(const Person *other)"<<endl;
p = other;
}
sp(sp &other){
cout<<"sp(const Person *other)"<<endl;
p = other.p;
}
~sp(){
cout<<"~sp()"<<endl;
if (p)
delete p;
}
Person *operator->(){
return p;
}
};
修改 test_func(void) 函数:
void test_func(sp &other)
{
sp s = other;
s->printInfo();
//Person *p = new Person();
//p->printInfo();
//delete p;
}
可以看到我们用 sp s 代替了 Person p ,我们在 sp 类里面定义了Person p,但是s是一个局部变量在test_fun(void)结束后就会自动调用析构函数~sp(),在 ~sp() 中我们实现了对 Person p的释放。修改main函数:
int main(int argc, char **argv)
{
int i;
/* sp other = new Person()==>相当于:
* 1. Person *p = new Person();
* 2. sp tmp(p); ==> sp(Person *other)
* 3.
* 3.1 sp other(tmp); ==> sp(sp &other2)
问题在于: sp &other2 = tmp; // 错误语法
const sp& other2 = tmp; //ok
* 或
* 3.2 sp other(tmp ==> Person*); ==>sp(Person *other)
*/
sp other = new Person();
for (i = 0; i < 2; i++)
test_func(other);
return 0;
}
编译执行代码,啪,怎么出错了?具体原因已经在 main 函数里进行注释注释,简言之就是 test_func(other) 需要执行两次,函数出入参数为引用,第一次结束时 sp other 就已经被释放了,第二次执行的时候肯定会粗错了,我们只需要修改构造函数:
sp(const sp &other)
{
cout<<"sp(const sp &other)"<<endl;
p = other.p;
}
再次编译执行,这下成功了:
对于刚才编译出错的问题我们还可以运用其他方法优化解决,大致的思路是:在构造函数里对引用对象的计数值加一,析构函数里对构造函数的计数值减一,只有当引用对象的计数值为零时才把引用对象给销毁掉。此外我们应该少用 "Person " ; 用 “sp” 来代替 “Person *p” ,但如果我们要用*(sp).XXX* 这种格式,我们就必须对 “*“ 就行重载”。为了方便阅读,直接给出完整代码:
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class Person {
private:
int count;
public:
void incStrong(){ count++; }
void decStrong(){ count--; }
int getStrongCount(){ return count;}
Person() : count(0){
cout <<"Pserson()"<<endl;
}
~Person()
{
cout << "~Person()"<<endl;
}
void printInfo(void)
{
cout<<"just a test function"<<endl;
}
};
class sp {
private:
Person *p;
public:
sp() : p(0) {}
sp(Person *other)
{
cout<<"sp(Person *other)"<<endl;
p = other;
p->incStrong();
}
sp(const sp &other)
{
cout<<"sp(const sp &other)"<<endl;
p = other.p;
p->incStrong();
}
~sp()
{
cout<<"~sp()"<<endl;
if (p)
{
p->decStrong();
if (p->getStrongCount() == 0)
{
delete p;
p = NULL;
}
}
}
Person *operator->()
{
return p;
}
Person& operator*()
{
return *p;
}
};
void test_func(sp &other)
{
sp s = other;
cout<<"In test_func: "<<s->getStrongCount()<<endl;
s->printInfo();
//Person *p = new Person();
//p->printInfo();
//delete p;
}
int main(int argc, char **argv)
{
int i;
/* 少用"Person *"; 用"sp"来代替"Person *"
* Person *per;
* 有2种操作: per->XXXx, (*per).XXX
* sp也应该有这2中操作:
* sp->XXX, (*sp).XXX
*
*/
sp other = new Person();
(*other).printInfo();
cout<<"Before call test_func: "<<other->getStrongCount()<<endl;
for (i = 0; i < 2; i++)
{
test_func(other);
cout<<"Af ter call test_func: "<<other->getStrongCount()<<endl;
}
return 0;
}
编译执行代码,输出结果与推理一致:
我们的程序是否已经完美了,还有可以优化的地方吗?当然是有的,对于对引用对象计数进行加减部分我们可以将它定义成基类,直接继承;对于sp部分我们还可以将它修改成模板…
#include <iostream>
#include <string.h>
#include <unistd.h>
using namespace std;
class RefBase {
private:
int count;
public:
RefBase() : count(0) {}
void incStrong(){ count++; }
void decStrong(){ count--; }
int getStrongCount(){ return count;}
};
class Person : public RefBase{
public:
Person() {
cout <<"Pserson()"<<endl;
}
~Person()
{
cout << "~Person()"<<endl;
}
void printInfo(void)
{
cout<<"just a test function"<<endl;
}
};
template<typename T>
class sp {
private:
T *p;
public:
sp() : p(0) {}
sp(T *other)
{
cout<<"sp(T *other)"<<endl;
p = other;
p->incStrong();
}
sp(const sp &other)
{
cout<<"sp(const sp &other)"<<endl;
p = other.p;
p->incStrong();
}
~sp()
{
cout<<"~sp()"<<endl;
if (p)
{
p->decStrong();
if (p->getStrongCount() == 0)
{
delete p;
p = NULL;
}
}
}
T *operator->()
{
return p;
}
T& operator*()
{
return *p;
}
};
template<typename T>
void test_func(sp<T> &other)
{
sp<T> s = other;
cout<<"In test_func: "<<s->getStrongCount()<<endl;
s->printInfo();
//Person *p = new Person();
//p->printInfo();
//delete p;
}
int main(int argc, char **argv)
{
int i;
/* 少用"Person *"; 用"sp<Person>"来代替"Person *"
* Person *per;
* 有2种操作: per->XXXx, (*per).XXX
* sp也应该有这2中操作:
* sp->XXX, (*sp).XXX
*
*/
sp<Person> other = new Person();
(*other).printInfo();
cout<<"Before call test_func: "<<other->getStrongCount()<<endl;
for (i = 0; i < 2; i++)
{
test_func(other);
cout<<"After call test_func: "<<other->getStrongCount()<<endl;
}
return 0;
}
最后再试验一下: