造成內存洩露的大多原因都是因為分配了空間但是沒有進行釋放的結果。但是這樣並不會引發錯誤 一般來說,解決這個問題的有很多種方法,比如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: [cpp] #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環境): [cpp] // 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: [cpp] #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環境): [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" #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: [cpp] #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環境): [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" #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; }