Complete Example for Student Class:#include<iostream.h> template<class T> class Smart; class Std{ friend class Smart<Std>; int count; char name[100]; public: Std(char *name) { cout<<"Student cons created"<<endl; count=0; strcpy(this->name,name); } ~Std() { cout<<"Student des called"<<endl; } void displayName() { cout<<" Name is :"<<name<<endl; } }; template<class T> class Smart{ T *p; public: Smart() { p=NULL; } Smart(T* a) { p=a; ++p->count; cout<<"Object created!"<<p->count<<endl; } Smart(const Smart &a) { p=a.p; p->count++; cout<<"Object copied!"<<p->count<<endl; } Smart& operator=(Smart const &a) { T* const old = p; p = a.p; p->count++; if(old->count==0) delete old; cout<<"Object assigned"<<p->count<<endl; return *this; } ~Smart() { if(--p->count==0) delete p; } T* operator->() { return p; } T& operator*() { return *p; } }; int main() { Smart<Std> obj(new Std("sai")); Smart<Std> obj1(obj); Smart<Std> obj2=obj1; // obj2=obj1; obj2->displayName(); return 0; }
class FredPtr; class Fred { public: Fred() : count_(0) /*...*/ { } // All ctors set count_ to 0 ! ... private: friend class FredPtr; // A friend class unsigned count_; // count_ must be initialized to 0 by all constructors // count_ is the number of FredPtr objects that point at this }; class FredPtr { public: Fred* operator-> () { return p_; } Fred& operator* () { return *p_; } FredPtr(Fred* p) : p_(p) { ++p_->count_; } // p must not be NULL ~FredPtr() { if (--p_->count_ == 0) delete p_; } FredPtr(FredPtr const& p) : p_(p.p_) { ++p_->count_; } FredPtr& operator= (FredPtr const& p) { // DO NOT CHANGE THE ORDER OF THESE STATEMENTS! // (This order properly handles self-assignment) // (This order also properly handles recursion, e.g., if a Fred contains FredPtrs) Fred* const old = p_; p_ = p.p_; ++p_->count_; if (--old->count_ == 0) delete old; return *this; } private: Fred* p_; // p_ is never NULL };Naturally you can use nested classes to rename FredPtr to Fred::Ptr.
Note that you can soften the "never NULL" rule above with a little more checking in the constructor, copy constructor, assignment operator, and destructor. If you do that, you might as well put a p_ != NULL check into the "*" and "->" operators (at least as an assert()). I would recommend against an operator Fred*() method, since that would let people accidentally get at the Fred*.
One of the implicit constraints on FredPtr is that it must only point to Fred objects which have been allocated via new. If you want to be really safe, you can enforce this constraint by making all of Fred's constructors private, and for each constructor have a public (static) create() method which allocates the Fred object via new and returns a FredPtr (not a Fred*). That way the only way anyone could create a Fred object would be to get a FredPtr("Fred* p = new Fred()" would be replaced by "FredPtr p = Fred::create()"). Thus no one could accidentally subvert the reference counting mechanism.
For example, if Fred had a Fred::Fred() and a Fred::Fred(int i, int j), the changes to class Fred would be:
class Fred { public: static FredPtr create(); // Defined below class FredPtr {...}; static FredPtr create(int i, int j); // Defined below class FredPtr {...}; ... private: Fred(); Fred(int i, int j); ... }; class FredPtr { /* ... */ }; inline FredPtr Fred::create() { return new Fred(); } inline FredPtr Fred::create(int i, int j) { return new Fred(i,j); }The end result is that you now have a way to use simple reference counting to provide "pointer semantics" for a given object. Users of your Fred class explicitly use FredPtr objects, which act more or less like Fred* pointers. The benefit is that users can make as many copies of their FredPtr "smart pointer" objects, and the pointed-to Fred object will automagically get deleted when the last such FredPtr object vanishes.
No comments:
Post a Comment