四、指针
造成内存泄露的大多原因都是因为分配了空间但是没有进行释放的结果。但是这样并不会引发错误
一般来说,解决这个问题的有很多种方法,比如com、boost的智能指针,都在某一种情况下对这种问题提供了解决方案。下面来探讨两种情况的指针
但我们可以建立一个自己的smart point,同时也需要考虑三种情况:
1.是否允许对smart point进行复制,如果是,在smart point中的多份拷贝中,到底有哪一个负责删除它们共同指向的对象(com是最后一个)
2.smart point是否表示指向一个对象的指针,或者表示指向一个对象数组的指针(即它应该使用带方括号的还是不带方括号的delete操作符)
3.smart point是否对应于一个常量指针或一个非常量指针
出于上述的三个情况,有以下两种smart point可以满足:
1.引用计数指针(又称共享指针,也就是com中的smart point)
2.作用域指针
这两种smart point的不同之处在于引用计数指针可以被复制,而作用域指针不能被复制。但是,作用域指针的效率更高。
引用计数指针:
scpp_refcountptr.h:
#ifndef __SCCP_SCOPEDPTR_H__
#define __SCCP_SCOPEDPTR_H__
#include "scpp_assert.h"
namespace scpp
{
template <typename T>
class RefCountPtr
{
public:
explicit RefCountPtr(T* p = NULL)
{
Create(p);
}
RefCountPtr(const RefCountPtr<T>& rhs)
{
Copy(rhs);
}
RefCountPtr<T>& operator = (const RefCountPtr<T>& rhs)
{
if (ptr_ != rhs.ptr_)
{
Kill();
Copy(rhs);
}
return *this;
}
RefCountPtr<T>& operator = (T *p)
{
if (ptr_ != p)
{
Kill();
Create(p);
}
return *this;
}
~RefCountPtr()
{
std::cout << "kill" << std::endl;
Kill();
}
public:
T* Get() const { return ptr_; }
T* operator ->() const
{
std::cout << "in this" << std::endl;
SCPP_TEST_ASSERT(ptr_ != NULL, "Attempt to use operator -> on NULL pointer.");
return ptr_;
}
T& operator *() const
{
SCPP_TEST_ASSERT(ptr_ != NULL, "Attempt to use operator -> on NULL pointer.");
return *ptr_;
}
private:
void Create(T* p)
{
ptr_ = p;
if (ptr_ != NULL)
{
refCount_ = new int;
*refCount_ = 1;
}
else
{
refCount_ = NULL;
}
}
void Copy(const RefCountPtr<T>& rhs)
{
ptr_ = rhs.ptr_;
refCount_ = rhs.refCount_;
if (refCount_ != NULL)
{
++(*refCount_);
}
}
void Kill()
{
if (refCount_ != NULL)
{
if (--(*refCount_) == 0)
{
delete ptr_;
ptr_ = NULL;
delete refCount_;
refCount_ = NULL;
}
}
}
private:
T* ptr_;
int* refCount_;
};
} // namespace scpp
#endif // __SCCP_SCOPEDPTR_H__
测试代码(vs2012+win7环境):
// debug.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "scpp_assert.h"
#include "iostream"
#include "scpp_vector.h"
#include "scpp_array.h"
#include "scpp_matrix.h"
#include "algorithm"
#include "scpp_types.h"
#include "scpp_refcountptr.h"
#define STUDENTAGE 10
class Student
{
public:
Student(int age) : age_(age)
{
}
void ShowAge()
{
std::cout << "my age is : " << age_ << std::endl;
}
private:
int age_;
};
int _tmain(int argc, _TCHAR* argv[])
{
scpp::RefCountPtr<Student> smartPoint(new Student(STUDENTAGE));
scpp::RefCountPtr<Student> smartPoint2;
smartPoint2 = smartPoint; //@test:赋值操作符
smartPoint = NULL;
smartPoint2->ShowAge();
scpp::RefCountPtr<Student> smartPoint3(smartPoint2); //@test:拷贝构造函数
smartPoint2 = NULL;
smartPoint3->ShowAge();
scpp::RefCountPtr<Student> *smartPoint4; //@test:引用
smartPoint4 = &smartPoint3;
smartPoint4->Get()->ShowAge();
Student studen3 = *smartPoint3; //@test:Get()
studen3.ShowAge();
smartPoint3 = NULL;
Student *student4 = new Student(STUDENTAGE); //@使用方式
scpp::RefCountPtr<Student> smartPoint5;
smartPoint5 = student4;
smartPoint5->ShowAge();
return 0;
}
作用域指针:
如果不打算复制智能指针(因此拷贝构造函数和赋值操作符被声明为私有),只是想保证被分配的资源将被正确地回收时使用这种方式,将会减少计数指针保存技术的int*的空间
scpp_scopedptr.h:
#ifndef __SCCP_SCOPEDPTR_H__
#define __SCCP_SCOPEDPTR_H__
#include "scpp_assert.h"
namespace scpp
{
template <typename T>
class ScopedPtr
{
public:
explicit ScopedPtr(T* p = NULL)
: ptr_(p)
{
}
ScopedPtr<T>& operator = (T* p)
{
if (ptr_ != p)
{
delete ptr_;
ptr_ = p;
}
return *this;
}
~ScopedPtr()
{
delete ptr_;
}
T* operator -> () const
{
SCPP_TEST_ASSERT(ptr_ != NULL, "Attempt to use operator -> on NULL pointer.");
return ptr_;
}
T& operator * () const
{
SCPP_TEST_ASSERT(ptr_ != NULL, "Attempt to use operator -> on NULL pointer.");
return *ptr_;
}
T* Release()
{
T* p = ptr_;
ptr_ = NULL;
return p;
}
private:
T* ptr_;
ScopedPtr(const ScopedPtr<T>& rhs);
ScopedPtr<T>& operator = (const ScopedPtr<T>& rhs);
};
} // namespace scpp
#endif // __SCPP_SCOPEDPTR_HPP_INCLUDED__
测试代码(vs2012+win7环境):
#include "stdafx.h"
#include "scpp_assert.h"
#include "iostream"
#include "scpp_vector.h"
#include "scpp_array.h"
#include "scpp_matrix.h"
#include "algorithm"
#include "scpp_types.h"
#include "scpp_refcountptr.h"
#include "scpp_scopedptr.h"
#define STUDENTAGE 10
class Student
{
public:
Student(int age) : age_(age)
{
}
void ShowAge()
{
std::cout << "my age is : " << age_ << std::endl;
}
private:
int age_;
};
int _tmain(int argc, _TCHAR* argv[])
{
scpp::ScopedPtr<Student> smartPoint(new Student(STUDENTAGE)); //@test:构造函数
smartPoint->ShowAge();
scpp::ScopedPtr<Student> smartPoint2; //@test:实体类的赋值操作
Student *student = new Student(STUDENTAGE); //@使用方式
smartPoint2 = student;
smartPoint2->ShowAge(); //@test:重载->
(*smartPoint2).ShowAge(); //@test:重载*
scpp::ScopedPtr<Student> *smartPoint3; //@test:Release()
smartPoint3 = &smartPoint2;
Student *students2;
students2 = smartPoint3->Release(); //释放smartpoint保存原来的地址
students2->ShowAge();
return 0;
}
综合两个smart point的例子,使用方式都是“每次使用new操作符创建一个对象时,立即把结果赋值给一个智能指针”。
以上的两个smart point其实都没解决一个问题,就是当我们的对象是const的情况,怎么办?分析一下这种情况,传入smart point的对象是一个const,这以为这里面的一些成员变量是无法修改的,所以有了下面的一种半智能的smart point:
scpp_ptr.h:
#ifndef __SCCP_PTR_H__
#define __SCCP_PTR_H__
#include "scpp_assert.h"
namespace scpp
{
template <typename T>
class Ptr
{
public:
explicit Ptr(T *p = NULL)
: ptr_(p)
{
}
T* Get() const
{
return ptr_;
}
Ptr<T>& operator = (T *p)
{
ptr_ = p;
return *this;
}
T& operator * () const
{
SCPP_TEST_ASSERT(ptr_ != NULL, "Attempt to use operator -> on NULL pointer.");
return *ptr_;
}
T* operator -> () const
{
SCPP_TEST_ASSERT(ptr_ != NULL, "Attempt to use operator -> on NULL pointer.");
return ptr_;
}
~Ptr()
{
}
private:
T* ptr_;
};
} // namespace scpp
#endif // __SCCP_REFCOUNTPTR_H__
测试代码(vs2012+win7环境):
#include "stdafx.h"
#include "scpp_assert.h"
#include "iostream"
#include "scpp_vector.h"
#include "scpp_array.h"
#include "scpp_matrix.h"
#include "algorithm"
#include "scpp_types.h"
#include "scpp_refcountptr.h"
#include "scpp_scopedptr.h"
#include "scpp_ptr.h"
#define STUDENTAGE 10
class Student
{
public:
Student(int age) : age_(age)
{
}
void ShowAge()
{
std::cout << "my age is : " << age_ << std::endl;
}
int GetAge() const
{
return age_;
}
void SetAge(int age)
{
age_ = age;
}
private:
int age_;
};
int _tmain(int argc, _TCHAR* argv[])
{
Student *student1 = new Student(STUDENTAGE); //@test:operator -> (T *p)
scpp::Ptr<Student> smartPoint1;
smartPoint1 = student1;
smartPoint1->ShowAge();
Student *student2 = new Student(STUDENTAGE); //@test:operator * (T *p)
scpp::Ptr<Student> smartPoint2;
smartPoint2 = student2;
(*smartPoint2).ShowAge();
Student *student3 = new Student(STUDENTAGE); //@test:operator * (T *p)
scpp::Ptr<const Student> smartPoint3;
smartPoint3 = student3;
std::cout << "this student age is : " << smartPoint3->GetAge() << std::endl;
smartPoint3->SetAge(STUDENTAGE); //@因为被限制,所以无法使用,也没必要使用
return 0;
}