// Illustrate the smart pointer approach using Templates // George F. Riley, Georgia Tech, Spring 2012 // This is nearly identical to the earlier handout on smart pointers // but uses a different syntax to "Create" the objects. Note // the Ptr constructor is templated to allow up to three arguments // which are passed to the "T" constructor. The "Create" methods // of the prior approach are not needed in this design. #include #include using namespace std; // The Ptr class contains two members, one a pointer to a generic // template type "T", and the int pointer for the reference count template class Ptr { public: Ptr(); template Ptr(T1); template Ptr(T1, T2); template Ptr(T1, T2, T3); // We need a copy constructor, assignment operator, and destructor Ptr(const Ptr&); // Copy constructor Ptr& operator=(const Ptr& rhs); // Assignment operator ~Ptr(); // Destructor // And we need a de-referencing operator, which should return // the dereferenced "t" pointer; const T& operator*() const; // De-referencing operator T& operator*(); // Non-const version // And the "arrow" operator. Need both const and non-const T* operator->(); const T* operator->() const; private: // Members are private T* t; int* refCount; }; // Ptr Implementations #ifdef OLD template Ptr::Ptr(T* t0) : t(t0) { // Constructor refCount = new int; // Create the refcount variable *refCount = 1; // Initially only one reference (this one). }; #endif template Ptr::Ptr() { t = new T; refCount = new int; // Create the refcount variable *refCount = 1; // Initially only one reference (this one). } template template Ptr::Ptr(T1 t1) { t = new T(t1); refCount = new int; // Create the refcount variable *refCount = 1; // Initially only one reference (this one). } template template Ptr::Ptr(T1 t1, T2 t2) { t = new T(t1,t2); refCount = new int; // Create the refcount variable *refCount = 1; // Initially only one reference (this one). } template template Ptr::Ptr(T1 t1, T2 t2, T3 t3) { t = new T(t1,t2,t3); refCount = new int; // Create the refcount variable *refCount = 1; // Initially only one reference (this one). } // Copy Constructor template Ptr::Ptr(const Ptr& rhs) : refCount(rhs.refCount), t(rhs.t) { // Increment the refCount (*refCount)++; } // Assignment operator template Ptr& Ptr::operator=(const Ptr& rhs) { // Protect against self assignment if (&rhs == this) return *this; // First reduce refcount in lhs and free if needed (*refCount)--; if (*refCount == 0) { // No remaining references delete t; delete refCount; } refCount = rhs.refCount; t = rhs.t; (*refCount)++; // Another reference return *this; } // Destructor template Ptr::~Ptr() { (*refCount)--; if (*refCount == 0) { // No remaining references delete t; delete refCount; } } // Dereferencing operators template const T& Ptr::operator*() const { return *t; } template T& Ptr::operator*() { return *t; } // Arrow operators template const T* Ptr::operator->() const { return t; } template T* Ptr::operator->() { return t; } // Now create classes A and B that we will use to illustrate the smart // pointers. class A { public: // constructors and destructors A(); A(int); A(const A&); ~A(); void Hello() const; public: int a; public: static int constructorCount; // Counts total number of constructors static int destructorCount; // Counts total number of destructors }; class B { public: B(); B(int, int, int); // three args B(const B&); ~B(); void Hello() const; public: int b0; int b1; int b2; public: static int constructorCount; // Counts total number of constructors static int destructorCount; // Counts total number of destructors }; // Implement A and B A::A() : a(0) { constructorCount++; }; A::A(int a0) : a(a0) { constructorCount++; }; A::A(const A& rhs) : a(rhs.a) { constructorCount++; }; A::~A() { // destructor destructorCount++; }; void A::Hello() const { cout << "Hello from A, a is " << a << endl; } B::B() : b0(0), b1(0), b2(0) { constructorCount++; }; B::B(int b00, int b01, int b02) : b0(b00), b1(b01), b2(b02) { constructorCount++; }; B::B(const B& rhs) : b0(rhs.b0), b1(rhs.b1), b2(rhs.b2) { constructorCount++; }; B::~B() { // destructor destructorCount++; }; void B::Hello() const { cout << "Hello from B, b0 is " << b0 << endl; } // Helper functions void PrintCounts() { cout << "A constructor " << A::constructorCount << " A destructor " << A::destructorCount << endl; cout << "B constructor " << B::constructorCount << " B destructor " << B::destructorCount << endl; }; // Define the A and B static variables int A::constructorCount = 0; int A::destructorCount = 0; int B::constructorCount = 0; int B::destructorCount = 0; void Test1() { // Just create a single A and B (with Create) and do not delete Ptr pA(10); Ptr pB(10,20,30); // just exit; smart pointer semantics call destructors automatically } void Test2() { // Just create a single A and B (with Create), then use Ptr copy constructor // to create two more; no deletes Ptr pA(10); Ptr pACopy(pA); Ptr pB(10, 20, 30); Ptr pBCopy(pB); // just exit; smart pointer semantics call destructors automatically } void Test3Helper(Ptr aByValue, Ptr bByValue) { // Also illustrates the dereferencing operator cout << "Hello from Test3Helper a is " << (*aByValue).a << " b0 is " << (*bByValue).b0 << endl; // Again, using arrow operator cout << "Hello agin from Test3Helper a is " << aByValue->a << " b0 is " << bByValue->b0 << endl; } void Test3() { // Create an A and B, pass by value to a function Ptr pA(10); Ptr pB(10, 20, 30); Test3Helper(pA, pB); } void Test4Helper(const Ptr& a, // by const reference const Ptr& b) { // Illustrate calling A and B member functions using // arrow operator a->Hello(); b->Hello(); } void Test4() { // Create an A and B, pass by const reference to a function Ptr pA(10); Ptr pB(10, 20, 30); Test4Helper(pA, pB); } void Test5() { // Create an two A and two B Ptr objects, then use assignment // operator. Ptr pA (10); Ptr pB (10, 20, 30); Ptr pA1(100); Ptr pB1(100, 200, 300); // Use assignment operator pA = pA1; pB = pB1; pA->Hello(); pB->Hello(); } void Test6() { // Create a vector of 100 Ptr objects vector > v; for (size_t i = 0; i < 100; ++i) { v.push_back(Ptr(i)); } // and just exit, no delete or clean up } // Main program int main(int argc, char** argv) { if (argc < 2) { cout << "Usage: ./template-smart-pointers (testNum)" << endl; exit(1); } int testNum = atol(argv[1]); switch (testNum) { case 1: Test1(); PrintCounts(); break; case 2: Test2(); PrintCounts(); break; case 3: Test3(); PrintCounts(); break; case 4: Test4(); PrintCounts(); break; case 5: Test5(); PrintCounts(); break; case 6: Test6(); PrintCounts(); break; } }