您的位置:首页 > 编程语言 > C语言/C++

简单的C++智能指针

2009-08-01 20:53 465 查看
本文链接:简单的C++智能指针

­

从书上看到了一个C++智能指针的例子,感觉不错,小改进了下,放出来。

智能指针的大意是:对普通的类(假设C)指针进行包装,内部维护一个引用计数。程序中只要使用智能指针包装类(假设SafeC)就行了。用的时候只用new C(), 然后把其给包装类进行构造,然后就随便用啦,不用担心C的析构问题了。

众所周知,C++中如果需要动态申请内存的话,就一定要释放。可是不是所有new出来的对象都能明确的知道该在哪里释放。释放的话,不知道在哪里释放才好,总不能某处正在使用着呢,就给delete了;不释放的话,就是传说中的内存泄漏。

所以,智能指针(好像也有叫安全指针的)的作用就显出来了。把一个申请来的对象指针,扔给智能指针去管理。智能指针会在对其所有引用都释放后,把内部管理的指针所指向的对象删除。使用起来的效果就像.Net和Java的引用一样。不过垃圾回收的机理应该和这个有些不同。

智能指针只使用值传递就可以,没有必要使用指针传递或引用传递,虽然效率高一点点。需要注意的是,智能指针就不要去new了,不然就失去其本来的意义了。

代码我改动了不少,还添加了测试代码。书上的代码只有很简单的非泛型的SafeC类。各种优缺点大家自己体会吧。(和Java或者是.Net对比一下,发现了这样写的优点了吧~)

另外说明一点:这个代码是没法在普通的C++环境下编译的,因为用的是C++.Net……用的.Net的控制台输出。大家把所有的输出代码删掉或改用C++的输出流就可以运行了(不要告诉我你看不出来哪个是.Net的东西啊……)。

另外:这个安全指针不是线程安全的,多线程中跨线程使用会出问题,需要添加线程锁。加锁一种可能的做法是:再维护一个锁对象的指针,在所有能接收到C新实例的指针的构造器中进行初始化,并与C的指针一同析构,在所有可能改变引用计数的地方进行线程同步,如构造器,赋值操作符,析构器。

代码:(自己写主函数)

template<class C>
class SafeC {
C* instance;
int* count;
private:
void ShowCount() {
Console::WriteLine("reference count in SafeC.constructor: " + *count);
}
public:
SafeC(int i):
instance(new C(i)), count(new int(1)) {
ShowCount();
}
SafeC(C* aInstance):
instance(aInstance), count(new int(1)) {
ShowCount();
}
SafeC(const SafeC& other):
instance(other.instance), count(other.count) {
++(*count);
ShowCount();
}
C* operator ->() {
return instance;
}
C* operator =(const SafeC& other) {
if(other.instance==instance) {
return *this;
} else {
~SafeC();
instance=other.instance;
count=other.count;
++(*count);
return *this;
}
}
~SafeC() {
Console::WriteLine("in SafeC.destructor, before excute, count: "+*count);
Console::WriteLine(" and the instance's id: "+instance->GetId());
if(--(*count) == 0) {
Console::WriteLine(" and delete the pointer to instance !! instance's id: " + instance->GetId());
delete instance;
delete count;
}
}
};

相关测试代码:

class C1 {
protected:
int id;
public:
C1(int i):id(i) {
Console::WriteLine("! in C1.C1, id: " + id);
}
virtual void Func() {
Console::WriteLine("! in C1.f, id: " + id);
}
virtual int GetId() {
return id;
}
virtual ~C1() {
Console::WriteLine("! in C1.~C1, id: " + id);
}
};

class C2 : public C1 {
public:
C2(int i):C1(i) {
}
virtual void Func() {
Console::WriteLine("! in C2.f, id: " + id);
}
virtual ~C2() {
Console::WriteLine("! in C2.~C2, id: " + id);
}
};

void TestSafeC() {
Console::WriteLine(">>>>> object in stack test:");
SafeC<C1> s1(new C1(1));
Console::WriteLine("s1");
SafeC<C1> s2(s1);
Console::WriteLine("s2");
SafeC<C1>& rs3 = s2;
Console::WriteLine("rs3");
SafeC<C1> s4 = s2;
Console::WriteLine("invoke: s4->Func()");
s4->Func();
Console::WriteLine("<<<<< end stack test.");
Console::WriteLine(">>>>> object in heap test:");
Console::WriteLine("ps5");
SafeC<C2>* ps5 = new SafeC<C2>(2); // not recommended. don't new SafeC, or it will be like a ordinary pointer.
Console::WriteLine("rs6");
SafeC<C2>& rs6 = *new SafeC<C2>(3); // notice! no delete operation on this. this is dangerous.
Console::WriteLine("s7");
SafeC<C2> s7 = *ps5;
Console::WriteLine("delete ps5");
delete ps5;
Console::WriteLine("<<<<< end heap test.");
Console::WriteLine(">>>>> method return value test:");
C1* InnerFunc();
SafeC<C1> s8(InnerFunc()); // the most important point~
Console::WriteLine("<<<<< end method return value test.");
}

C1* InnerFunc() {
return new C1(10);
}

输出:

­In Singleton's Constructor.
>>>>> object in stack test:
! in C1.C1, id: 1
reference count in SafeC.constructor: 1
s1
reference count in SafeC.constructor: 2
s2
rs3
reference count in SafeC.constructor: 3
invoke: s4->Func()
! in C1.f, id: 1
<<<<< end stack test.
>>>>> object in heap test:
ps5
! in C1.C1, id: 2
reference count in SafeC.constructor: 1
rs6
! in C1.C1, id: 3
reference count in SafeC.constructor: 1
s7
reference count in SafeC.constructor: 2
delete ps5
in SafeC.destructor, before excute, count: 2
and the instance's id: 2
<<<<< end heap test.
>>>>> method return value test:
! in C1.C1, id: 10
reference count in SafeC.constructor: 1
<<<<< end method return value test.
in SafeC.destructor, before excute, count: 1
and the instance's id: 10
and delete the pointer to instance !! instance's id: 10
! in C1.~C1, id: 10
in SafeC.destructor, before excute, count: 1
and the instance's id: 2
and delete the pointer to instance !! instance's id: 2
! in C2.~C2, id: 2
! in C1.~C1, id: 2
in SafeC.destructor, before excute, count: 3
and the instance's id: 1
in SafeC.destructor, before excute, count: 2
and the instance's id: 1
in SafeC.destructor, before excute, count: 1
and the instance's id: 1
and delete the pointer to instance !! instance's id: 1
! in C1.~C1, id: 1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: