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

Clone Smart Pointer (clone_ptr)

By , 22 Aug 2005
 

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

About the Author

Axter
Software Developer (Senior)
United States United States
Member
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 | :)

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralUpdated version of the code in this articlememberAxter30 Oct '05 - 14:34 
The following link contains an updated versions of the clone_ptr smart pointer:
http://code.axter.com/copy_ptr.h
 
The copy_ptr is a more optimized version of the clone_ptr.
Also for anyone interested, the following is a COW (Copy On Write) smart pointer that uses similar impementation to the copy_ptr smart pointer:
http://code.axter.com/cow_ptr.h
 
The following is a policy based smart pointer, that includes most of the logic in copy_ptr and cow_ptr:
http://axter.com/smartptr
 

Top ten member of C++ Expert Exchange.
http://www.experts-exchange.com/Cplusplus
 
-- modified at 10:51 Friday 28th April, 2006
GeneralRe: Problem with operator[]memberAndreas Tirok27 Aug '05 - 1:56 
Thanks. I will do this.
 
Another question:
 
Whats about the assign operator?
Can you point me to a solution?
 
TIA
 
Regards
 
Andy
 
I tried:
 
bool DbcRecord::SetColumnValue(std::string a_CName, std::string a_Value)
{
bool l_Ret = false;
try
{
clone_ptr l_Column;
/*
c:\home\andy\beusenknv\knv32\knv_module\knv_cable\knv_cable\dbcrecord.cpp(354) : error C2512: 'clone_ptr' : no standard constructor available
*/
if (Lookup("Test", l_Column))
{
// do something
l_Ret = true;
}
}
catch (...)
{
Log_t log;
log.SetSeverity(SV_CRITICAL);
log << "Exception in DbcRecord::SetColumnValue !!!\n" << commit;
}
return l_Ret;
}
 
bool DbcRecord::Lookup(std::string a_CName, clone_ptr& a_Column)
{
bool l_Ret = false;
try
{
DBCRECORD::iterator cim = m_Record.begin();
for (; cim != m_Record.end(); cim++)
{
clone_ptr l_Column = (*cim);
std::string l_CName;
if (l_Column->GetCName(l_CName) && l_CName == a_CName)
{
a_Column = l_Column;
l_Ret = true;
break;
}
}
}
catch (...)
{
Log_t log;
log.SetSeverity(SV_CRITICAL);
log << "Exception in DbcRecord::SetColumnValue !!!\n" << commit;
}
return l_Ret;
}
 

GeneralRe: Problem with operator[]memberAxter27 Aug '05 - 2:17 
The DbcColumn class needs to have a copy constructor for it to work with the clone_ptr.
 
The clone pointer clone's the object by calling the copy constructor.
 
Top ten member of C++ Expert Exchange.
http://www.experts-exchange.com/Cplusplus
GeneralProblem with operator[]memberAndreas Tirok27 Aug '05 - 0:18 
Hi,
 
I tried to use clone_ptr with a map.
When I use a normal iteration loop it works fine.
When I try to access the data vie key I get an compiler error.
 
Unfortunately I'am not very firm with C++ ... Frown | :-(
 
I hope, you can help me.
 
Regards.
 
Andy
 
#### snip ####
 

 
class DbcColumn
{
private:
long m_Colid;
std::string m_CName;
std::string m_Value;
 
public:
DbcColumn();
DbcColumn(const long a_Colid, const std::string a_CName, const std::string a_Value);
virtual ~DbcColumn();
 
bool operator<(const DbcColumn& rhs)const;
bool operator==(const DbcColumn& rhs)const;
 
bool GetColid(long& a_Colid);
bool GetCName(std::string& a_CName);
bool GetValue(std::string& a_Value);
 
bool SetValue(const std::string a_Value);
 
void Dump(Log_t& a_Log);
};
 
typedef std::list > DBCRECORD;
 
/** \brief The DbcRecord class provides an interface to a database row.
*
It is an substitute for TABBOXCOLUMNLIST. Additional this class works with clone_ptr.
The two operators are necessary when used with sorted containers.
\sa @ref clone_ptr
*/
class DbcRecord
{
private:
std::string m_TName;
DBCRECORD m_Record;
 
public:
DbcRecord(const std::string a_TName, const DBCRECORD& a_Record);
virtual ~DbcRecord();
 
bool operator<(const DbcRecord& rhs)const;
bool operator==(const DbcRecord& rhs)const;
 
bool GetTName(std::string& a_TName);
bool GetRecord(DBCRECORD& a_Record);
 
bool GetColumnValue(const std::string a_CName, std::string& a_Value, long& a_ColId);
bool SetColumnValue(std::string a_CName, std::string a_Value);
 
void Dump(Log_t& a_Log);
};
 
typedef std::map > DBCRECORDMAP;
 

 
bool DbcRecord::GetColumnValue(const std::string a_CName, std::string& a_Value, long& a_ColId)
{
bool l_Ret = false;
try
{
// DBCRECORD::iterator cim = m_Record.begin();
DBCRECORD::iterator cim = m_Record[a_CName];
 
/*
for (; cim != m_Record.end(); cim++)
{
clone_ptr l_pColumn = (*cim);
std::string l_CName;
std::string l_Value;
long l_ColId;
if (l_pColumn->GetCName(l_CName) && l_CName == a_CName)
{
if (l_pColumn->GetColid(l_ColId) && l_pColumn->GetValue(l_Value))
{
a_ColId = l_ColId;
a_Value = l_Value;
l_Ret = true;
break;
}
}
}
*/
}
catch (...)
{
Log_t log;
log.SetSeverity(SV_CRITICAL);
log << "Exception in DbcRecord::GetColumnValue !!!\n" << commit;
}
return l_Ret;
}
 

Kompilierung läuft...
DbcRecord.cpp
c:\home\andy\beusenknv\knv32\knv_module\knv_cable\knv_cable\dbcrecord.cpp(321) : error C2676: Binaerer Operator '[' : 'class std::list,class std::allocator > >' definiert diesen Opera
tor oder eine Konvertierung in einen fuer den vordefinierten Operator geeigneten Typ nicht
Fehler beim Ausführen von cl.exe.
 
DbcRecord.obj - 1 Fehler, 0 Warnung(en)
 
#### snap ####
GeneralRe: Problem with operator[]memberAxter27 Aug '05 - 1:35 
>>DBCRECORD::iterator cim = m_Record[a_CName];
 
The above line is the problem. m_Record is a list container, and a list container does not have a index operator (operator[]).
That's because you can not randomly access a list, like you can a vector or a deque.
If you need that type of access, I recommend you use a vector container instead.
 
According to the C++ standard, the std::vector should be the default container.
You should use a list or deque container if you need to remove or delete objects from the center or from the beginning of the container.
Otherwise, if you're only removing and adding from the end of the container, the vector container should be used.
 
Top ten member of C++ Expert Exchange.
http://www.experts-exchange.com/Cplusplus
GeneralI don't see anything new that you're doing.memberWREY25 Aug '05 - 13:39 
Whatever you're doing with your Clone Smart Pointer can already be done using regular STL statements.
 
For example, if you have a vector of ObjectA and you want to end up with a vector of similar objects, you'd simply copy the vector of ObjectA over to a vector of ObjectB. Done!!!
 
AAMOF, that is exactly what you are doing in your CopyCorrectDerivedTypeDemo() function. You are copying your vector of vBaseClass objects over to your other vector of vBaseClass_Copy objects. (That you are using templates and performing the copy inside a 'for' statement is irrelevant and purely incidental.)
 
True, while the Boost shared_ptr class does not keep ownership of an object, testing the pointers' addresses should be enough to tell you if you have an object that is one and the same. How much more would you need to know if you are dealing with an object that is equal? But if you need to have the same object residing at two different locations, then just go ahead and copy it (because that is exactly what your sample is doing)!
 
Besides, I am not too sure I might want to have certain objects copied and residing at different locations. Just knowing that their addresses are the same would be good enough for me. If I ABSOLUTELY must have a replica of an object, then I'd simply copy it.
 
Other than working primarily with pointers, I don't see anything extra to what you're offering.
 
Personally, I like to work with pointers and it'd be for that reason alone that I'd be in favor of using your technique.
 
Smile | :)
 
William
 
Fortes in fide et opere!
GeneralRe: I don't see anything new that you're doing.memberAxter25 Aug '05 - 15:09 
>>Whatever you're doing with your Clone Smart Pointer can already be done using regular STL statements.
 
That's incorrect.
You can not copy a container of abstract objects from one container to the next.
You can copy the pointer itself, but you can't copy what it's pointing to.
 
The clone pointer gives you the ability to copy abstract objects.
 
In many cases, it may not be necessary to copy the object, and using a share_ptr can meet code requirements. But when a copy of the objected is needed, then clone_ptr would be a more practical method.
 
Top ten member of C++ Expert Exchange.
http://www.experts-exchange.com/Cplusplus
GeneralRe: I don't see anything new that you're doing.memberWREY26 Aug '05 - 6:37 
Nowhere in your article did you talk about abstract objects. You repeatedly made references to concrete objects.
 
"Abstract object" is a borrowed term from Java, since in C++ an abstract object does not exist mainly because such objects cannot be instantiated from an abstract type. Therefore if you cannot instantiate an object from an abstract type, how can you end up with an abstract object?
 
(I am using 'abstract type' as the opposite of 'concrete type'.)
 
In C++, some people use it to denote a derived concrete object that is immediate to its abstract base class to show that kind of relationship, which is what I see you're doing, and by using the 'new' operator, you're dynamically allocating space for a member variable (inside it) that you're then assigning to a pointer.
 
That pointer is what contains the address of that variable inside the ABC that you in turn refer to as an abstract object.
 
I don't see anything 'magic' about your smart pointer. What I see is something simply handy to have around! That's all.

 
William
 
Fortes in fide et opere!
 
-- modified at 12:44 Friday 26th August, 2005
GeneralRe: I don't see anything new that you're doing.membergeometer30 Aug '05 - 0:12 
I don't understand this dogmatic discussion about derived concrete objects or abstract objects. But I see a method to use stl-containers as polymorphic containers without supplying a clone-function to the stored objects, and that's exactly what I've been looking for since some years. Thank you very much for your work, Axter.
 
When it works fine, I don't care how to call the objects, but I will use it in my projects.
 
@Wrey: if you have found a better solution for this problem, I would be glad to read an article about it here at code-project.
 
Chris
General[Msg Deleted]memberWREY30 Aug '05 - 2:50 

GeneralRe: I don't see anything new that you're doing.memberWREY30 Aug '05 - 2:57 
geometer wrote:
I don't understand this dogmatic discussion about derived concrete objects or abstract objects. But I see a method to use stl-containers as polymorphic containers without supplying a clone-function to the stored objects, and that's exactly what I've been looking for since some years.
 

geometer wrote:
@Wrey: if you have found a better solution for this problem, I would be glad to read an article about it here at code-project.
 

I might do that, but only after you've spent some more years looking for a better solution.
 
Big Grin | :-D
 
William
 
Fortes in fide et opere!
GeneralRe: I don't see anything new that you're doing.memberAxter30 Aug '05 - 15:09 
Thanks for the positive feedback.Smile | :)
 
Top ten member of C++ Expert Exchange.
http://www.experts-exchange.com/Cplusplus

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 23 Aug 2005
Article Copyright 2005 by Axter
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid