Click here to Skip to main content
Click here to Skip to main content

Clone Smart Pointer (clone_ptr)

, 22 Aug 2005 Public Domain
Rate this:
Please Sign up or sign in to vote.
A non-sharing smart pointer class that can be used with STL containers like std::map, vector, list, set, and deque. The smart pointer has assignment operator and greater than operator that call the target object's equal operator.

Introduction

The clone_ptr class is a smart pointer that is specifically designed to work with STL containers, and it's able to duplicate itself to the correct derived type without requiring the base class to have a clone method. Moreover the clone pointer has operator methods for assignment, equal, and greater than that calls the object's assignment operator. That means when a sort is performed on a container of clone pointers, the object being pointed to is sorted, and not the pointer address.

Unlike share pointers, the clone_ptr class does not share its pointer, and it's based on the idea of strict pointer ownership logic. That means the content of one container of clone pointers can be copied to another container without having two containers pointing to the same data.

Background

I decided to create a new smart pointer, because the common available smart pointers are not well suited for STL containers. In accordance with C++ standards, the std::auto_ptr can not be used at all with STL containers. The boost shared_ptr class does not keep ownership of its pointer, and instead shares it. That means a shared_ptr type of smart pointer would not be a good choice if you need to copy one container to another, or one part of a container to another.

One of the main logic differences between the clone_ptr class and other smart pointers like boost::shared_ptr and auto_ptr, is that the clone_ptr attempts to emulate a concrete type via pointer interface, where as boost::shared_ptr and std::auto_ptr attempt to emulate a pointer. The disadvantage of emulating a pointer is that it brings in logic that is not usually beneficial, and often detrimental to the desired effect.

An example of undesirable pointer behavior is the comparison operator.

void CompareFoo(foo* foofoo1, foo* foofoo2)
{
    if (foofo1 == foofoo2)
    //Does NOT compare the foo object, and instead
    // only compares pointer address
    {
    ............


void CompareFoo(shared_ptr < foo >  foofoo1, shared_ptr < foo >  foofoo2)
{
    if (foofo1 == foofoo2)
    //Does NOT compare the foo object, and instead
    // only compares pointer address
    {
    ............

Normally, in the above code, the desired logic is to test if what one pointer is pointing to is equal in value as to what the other pointer is pointing to. However, because the above code is not de-referencing the pointers, it only actually compares the addresses of the pointers.

With a clone_ptr, the comparison is made on the object being pointed to and not on the address of the pointer.

void CompareFoo(clone_ptr < foo >  foofoo1, clone_ptr < foo > foofoo2)
{
    if (foofo1 == foofoo2)//Does compare the foo object
    {

In a container of smart pointers, it's rarely required, nor desired to make a comparison on the pointer address, and instead, it's more beneficial and practical to have the operators apply logic to the deference pointer, instead of the pointer itself. That's the main reason why the clone_ptr class does not try to do a 100% pointer emulation, and instead it's more of a hybrid in which it emulates pointers where it's beneficial, but emulates concrete types when applying most comparison operators.

Although the clone_ptr class was specifically designed for use with STL containers, it can still be useful and has practical applications outside of an STL container.

Using the code

There are many benefits to using the clone_ptr over other smart pointers, but the following highlights some of the main benefits.

The clone_ptr allows for the copying of an object, instead of copying the pointer address.

void CopyCorrectDerivedTypeDemo() {
std::vector < clone_ptr < BaseClass> >  vBaseClass; 
    //Setup data using base and derived type classes
    vBaseClass.push_back(new BaseClass( "1" )); 
    vBaseClass.push_back(new Derived_B( "2" )); 
    vBaseClass.push_back(new BaseClass( "3" )); 
    vBaseClass.push_back(new Derived_A( "4" )); 
    vBaseClass.push_back(new BaseClass( "5" )); 
    
    //Copy contents from one container to another
    std::vector < clone_ptr < BaseClass > >  
      vBaseClass_Copy(vBaseClass.begin(), vBaseClass.end()); 
    
    //Display results
    for (std::vector < clone_ptr < BaseClass > >::size_type i 
                   = 0;i < vBaseClass_Copy.size();++i)
    {
        vBaseClass_Copy[i]->WhoAmI();
    }

When sorting a container of clone_ptr, the object type is sorted instead of the pointer address.

void VectorSortDemo()
{
    std::vector < clone_ptr < BaseClass> >  vBaseClass; 
    //Setup data using base and derived type classes
    vBaseClass.push_back(new BaseClass( "3" )); 
    vBaseClass.push_back(new Derived_B( "2" )); 
    vBaseClass.push_back(new BaseClass( "1" )); 
    vBaseClass.push_back(new Derived_A( "5" )); 
    vBaseClass.push_back(new BaseClass( "4" )); 

    //Sort Data
    sort(vBaseClass.begin(), vBaseClass.end()); 

    //Display sorted data
    for (std::vector < clone_ptr < BaseClass > >::size_type 
                            i = 0;i < vBaseClass.size();++i)
    {
        vBaseClass[i]->WhoAmI();
    }

std::set and std::map containers also get correctly sorted when using the clone_ptr.

void SetDemo()
{
    std::set < clone_ptr < BaseClass > >  sBaseClass; 
    //Setup data using base and derived type classes
    sBaseClass.insert(new BaseClass( "3" )); 
    sBaseClass.insert(new Derived_B( "2" )); 
    sBaseClass.insert(new BaseClass( "1" )); 
    sBaseClass.insert(new Derived_A( "5" )); 
    sBaseClass.insert(new BaseClass( "4" )); 

    //Display sorted data
    for(std::set < clone_ptr < BaseClass > >::iterator i = 
                sBaseClass.begin();i!=sBaseClass.end();++i)
    {
        (*i)->WhoAmI();
    }

The equality operator, assists in determining if one container is equal to another.

void ContainerEqualityDemo()
{
    list < clone_ptr < BaseClass > >  PtrsX;
    PtrsX.push_back(new BaseClass("1"));
    PtrsX.push_back(new BaseClass("2"));
    PtrsX.push_back(new BaseClass("3"));
    
    list < clone_ptr < BaseClass > >  PtrsY;
    PtrsY.push_back(new Derived_B("4"));
    PtrsY.push_back(new Derived_B("5"));
    PtrsY.push_back(new Derived_B("6"));
    
    list < clone_ptr < BaseClass > > PtrsZ;
    PtrsZ.push_back(new Derived_A("1"));
    PtrsZ.push_back(new Derived_A("2"));
    PtrsZ.push_back(new Derived_A("3"));
    
    bool IsEqual1 = (PtrsX == PtrsY); //Should be false
    bool IsEqual2 = (PtrsX == PtrsZ); //Should be true
    
    cout << "Should be false (0) IsEqual1 = " << IsEqual1 << endl;
    cout << "Should be true (1) IsEqual2 = " << IsEqual2 << endl;

The equality operator also helps with STL replace and remove functions.

void RemoveFromListDemo()
{
    std::list < clone_ptr < BaseClass> >  lBaseClass; 
    lBaseClass.push_back(new BaseClass( "X" )); 
    lBaseClass.push_back(new Derived_B( "2" )); 
    lBaseClass.push_back(new BaseClass( "X" )); 
    lBaseClass.push_back(new Derived_A( "5" )); 
    lBaseClass.push_back(new BaseClass( "4" ));

    //Remove certain values
    lBaseClass.remove(clone_ptr < BaseClass > (new BaseClass("X"))); //Remove all X's
    
    //Display results
    for(std::list < clone_ptr < BaseClass > >::iterator 
        i = lBaseClass.begin();i!=lBaseClass.end();++i)
    {
        (*i)->WhoAmI();
    }

Conclusions

By using the clone_ptr, you get the advantages of a pointer, without the disadvantages of pointer operators and potential memory leaks.

All the above code is in the demo project listed for download.

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication

Share

About the Author

Axter
Software Developer (Senior)
United States United States
I started programming as a hobby in 1985 on my 8-bit Atari computer. I programmed in BASIC and in assembly language until I upgraded to a 16-bit IBM compatible in 1988.
In 1989, I started programming in C using Turbo C++. IMHO, Turbo C/C++ was the best compiler available to the market for many years, until VC++ 4.0 came out.
I started using MFC with VC++ 4.0 in 1996, and I developed and sold an application called GameMenu95. It had limited success, and I retired the program in 1999.
 
I now perform contract work in a variety of operating systems mostly using C/C++.
I occasionally have contract work for Java applications and MS Access development.
 
My favorite language of choice is C++. I believe it has the best combination of flexibility, speed, and type safety.
I’ve used many compilers and IDE, to include (VC++7.0, 7.1, 6.0, GCC 2.x, 3.x, Turbo C/C++, Borland 5.x, Watcom, MinGW, DevC++, Eclipse….)
However, by far, my most favorite IDE is VC++6.0. Although this compiler has poor C++ compliance, it more then makes up for it by its outstanding interface and tools.
 
Coding is still my hobby, and I’m very fortunate in that I get paid good money to engage in my hobby!
 
Life is good!!! Smile | :)
 

If I really helped you save some bucks, I would like to ask you for something: Please donate some of these saved bucks (you decide) to the charity organization of your choice - or just help out somebody who is needier than you are......
Pass it on....... Smile | :)

Comments and Discussions

 
General'cannot instantiate abstract class' Pinmemberwarlock6x330-Nov-06 2:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.141015.1 | Last Updated 23 Aug 2005
Article Copyright 2005 by Axter
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid