I am using the CUDD package for BDDs manipulation. I want to make a copy for a big data structure in it that is called the DdManager. The problem is : this data structure has so many pointers inside it , so when I make a direct copy it is a "shallow" copy(as some use the term) i.e. : the pointers in the new copy point to the same places pointed to by the original copy , so when I change in anyone of them I also change in the other which is undesirable .... Trying to make a copy function by hand is not feasible because the data structure is really big and very detailed with many pointer to other complex structures also !!! I have tried the vector solutions described here but I did not get the expected result because there are many nested structures and pointers and I want a completely new copy.

Here is a code sample of what I want to do :

#include <iostream>
#include <cstdlib>
#include <string.h>
#include <vector>
using namespace std;
struct n1
{
    int a;
    char *b;
};
struct n2
{
    int **c;
    struct n1 *xyz;
};
typedef struct
{
    vector<struct n2> x;
}X;
int main()
{
    struct n2 s1;
    s1.xyz = (struct n1*)malloc(sizeof(struct n1));
    s1.xyz->a = 3;
    s1.xyz->b = (char*)malloc(5);
    s1.xyz->b[0] = '\0';
    strcat(s1.xyz->b,"Mina");
    s1.c = (int**)malloc(5 * sizeof(int*));
    for(int i = 0; i < 5; i++)
        s1.c[i] = (int*)malloc(5 * sizeof(int));
    for(int i = 0; i < 5; i++)
        for(int j = 0 ; j < 5 ; j++)
            s1.c[i][j] = i + j;
    X struct1,struct2;
    vector<struct n2>::iterator it;
    it = struct1.x.begin();
    it = struct1.x.insert(it,s1);
    it = struct2.x.begin();
    it = struct2.x.insert(it,struct1.x[0]);
    cout<<"struct2.x[0].c[1][2]  = "<<struct2.x[0].c[1][2] <<" !"<<endl; // This is equal to 3
    (struct2.x[0].c[1][2])++; //Now it becomes 4
    cout<<"struct2.x[0].c[1][2]  = "<<struct2.x[0].c[2][2] <<" !"<<endl; //This will print 4
    cout<<"s1.c[1][2]  "<< s1.c[1][2]<<" !"<<endl; // This will also print 4 ... that's the wrong thing 
    return 0;
}

Recommended Answers

All 4 Replies

You have no choice but to allocate new memory and copy the data pointed to by the pointers. The most robust way to do this would be to write a copy constructor for each of the structures that needs to handle a deep copy.

And of course, when you write a copy constructor, you should probably also write a constructor, destructor, and copy assignment operator. This would greatly simplify your usage of those structures on top of ensuring that memory is properly handled in any case.

It looks like you're used to programming in C a bit more than C++. As Deceptikon mentioned, you should write some constructors to handle copying and other object instantiation events. This will greatly improve the level of encapsulation in your program.

In general, it's desirable for an object to handle its own resources. That way you can just know that all the memory is going to be correctly allocated when you want to use is and, when all your objects go out of scope, you know that all the memory will be cleaned up again as well. So, to take your n1 class, you could consider doing something like:

#include <iostream>
#include <cstring>

//=========================================================

class n1
{
public :
    /// Empty constructor initialises things to known values
    n1() : m_a(0), m_b(NULL) {}

    /// Constructor that takes a character string
    n1( const char* psz );

    /// Constructor that takes a character string and a value for a
    n1( const char* psz, int a );

    /// Copy constructor
    n1( const n1& other );

    /// Assignment operator
    const n1& operator=( const n1& other );

    /// Efficient swap method
    void swap( n1& other )
    {
        std::swap( m_a, other.m_a );
        std::swap( m_b, other.m_b );
    }

    /// Destructor
    ~n1();

    /// Get the value of a
    const int& a() const { return m_a; }

    /// Get the value of b
    const char* b() const { return m_b; }

private :
    /// Allocates enough heap memory to store psz, copies psz
    /// to it an returns a pointer to the new memory
    static char* M_callocString( const char* psz );

    /// Deallocate the memory for b
    void delete_b()
    {
        if ( m_b != NULL )
            delete[] m_b;

        m_b = NULL;
    }

    int m_a;
    char* m_b;
};

//=========================================================

char* n1::M_callocString( const char *psz )
{
    size_t length = std::strlen( psz );

    // Allocate some memory for b
    char* pszOut = new char[ length ];
    std::strncpy( pszOut, psz, length );

    return pszOut;
}

//=========================================================

n1::n1( const char *psz ) : m_a(0), m_b( M_callocString(psz) ) {}

//=========================================================

n1::n1( const char *psz, int a ) : m_a(a), m_b( M_callocString(psz) ) {}

//=========================================================

n1::n1( const n1& other ) : m_a( other.a() ), m_b( M_callocString(other.b() ) ) {}

//=========================================================

const n1& n1::operator=( const n1& other )
{
    if ( this == &other )
        return *this;

    n1 temp( other );
    this->swap( temp );
    return *this;
}

//=========================================================

n1::~n1() { delete_b(); }

//=========================================================

// Create a template specialisation for std::swap, this is quite important to
// do if you want to use your class in STL things
namespace std {
    template<>
    void swap ( n1& x1, n1& x2 ) { x1.swap( x2 ); }
}

//=========================================================

// Overload stream insertion
std::ostream& operator<<( std::ostream& os, const n1& x )
{
    os << "(" << x.a() << ",\"" << ( x.b() != NULL ? x.b() : "" ) << "\")";
    return os;
}

//=========================================================

int main()
{
    n1 x1;
    n1 x2( "Hello x1!");
    n1 x3( "I'm x3", 10 );

    n1 x4( x3 );

    n1 x5 = x2;

    std::cout << "x1 = " << x1 << std::endl;
    std::cout << "x2 = " << x2 << std::endl;
    std::cout << "x3 = " << x3 << std::endl;
    std::cout << "x4 = " << x4 << std::endl;
    std::cout << "x5 = " << x5 << std::endl;

    return 0;
}

I know this looks like a lot of code, but the idea is that the whole object takes care of itself. The important thing is that we can pretty much guarantee the state of the members m_a and m_b at any given time. If you do the same thing for n2 it it will be simple to see how a deep copy is done by recursively calling copy constructors on all the component objects.

First of all : Thanks a lot for your reply and for your great help !
Second : The code that is written is just a prototype of what I really want to do .... Remember that I said in the begining that I am using the CUDD package ... this package is written in C and with structures and I can not change that so I am stuck .. that's the main point of the problem ..... if I were using my own package , I would take your suggestion of course :-)

this package is written in C and with structures and I can not change that so I am stuck ..

It doesn't change the fact that you need to do a deep copy. If you have no choice but to do it manually, do it manually. Alternatively, you can write wrapper classes around these problematic structures to introduce automatic resource management.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.