473,509 Members | 2,918 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

Problem with copy constructor (a bit long story...)

Hi all,

I'm trying to implement a certain class but I have problems regarding
the copy ctor. I'll try to explain this as good as possible and show
what I tried thusfar. Because it's not about a certain code syntax but
more a 'code architecture' thing , I'll use simple example classes
(which are certainly not complete or working...) just to illustrate the
idea (and I may make some mistakes because I'm not that experienced...).

The basic idea is that I have a class which has a data vector, but
objects of that class can also share that data vector between several
instances:

class A {
vector<double*data; // pointer, so objects of 'A' can _share_ data.
bool data_is_shared; // does this 'A' share data with another A?

A(const bool do_data_init=true); // ctor
A(const A& a); // copy constructor
A get_shared_A(); // get an object A which shares 'this' data
};

Ctor:

A::A(bool do_data_init)
{
if (do_data_init)
data = new vector<double>;
data_is_shared = !do_data_init;
}

Copy ctor, always result in an 'A' which does not share data:

A::A(const A& a)
{
data = new vector<double>;
data_is_shared = false;
*data = *a.data;
}

Get an object A which shares data with 'this':

A A::get_shared_A()
{
A a(false);

a.data = data; // copy the data pointer only
return a;
}

The problems focus on this last method 'get_shared_A'. It should return
an object A that shares its data with 'this'. Problem: on returning 'a',
the copy constructor _may_ be called and then an object A is returned
which has its own data vector. So this doesn't work... I cannot change
the copy ctor because that should always return an A which does not
share its data (that's a requirement for correct behaviour of class A).

In my application, 'get_shared_A' is an operator which selects a part of
the original data in 'this', and the returned result must be an object A
because it must be used in mathematical expressions just like all other A's.

So, using a 'helper'-class B I tried to change this in:

A A::get_shared_A()
{
B b;

b.data = data; // copy the data pointer only
return b;
}

With class B:

class B {
vector<double*data;
};

And an extra conversion ctor for A:

A::A(const& B b)
{
data = b.data;
data_is_shared = true;
}

So I wanted the code to force to use this new ctor and return an object
A which shares its data. But again, after the conversion ctor is called
it is still possible that the copy ctor is also called and things do not
work correctly.

In fact, all possible solutions I came up with do not work because you
cannot be sure if the copy ctor is called when returning from
'get_shared_A', it depends on the compiler you have.

So, does anybody has any ideas to overcome this problem?

Jeroen
Apr 24 '07 #1
13 2438
* Jeroen:
>
The basic idea is that I have a class which has a data vector, but
objects of that class can also share that data vector between several
instances:
Use boost::shared_ptr to implement the sharing. If you want a
non-shared copy provide that via some member function. Not via the copy
constructor.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Apr 24 '07 #2
Alf P. Steinbach schreef:
* Jeroen:
>>
The basic idea is that I have a class which has a data vector, but
objects of that class can also share that data vector between several
instances:

Use boost::shared_ptr to implement the sharing. If you want a
non-shared copy provide that via some member function. Not via the copy
constructor.
That may just be the problem. The library in which class A resides uses
the concept of shared data and so on. But for the user all of this is
not available, nor visible. The user expects 'normal' behaviour of class
A. So if a user of the library uses a subscript operator in order to
select a portion of the data in an object A and directly uses that as a
parameter to a function, then the copy constructor is called which is
required to make a non data-sharing copy:

void user_function(A a)
{
a = 10; // parameter 'a' must be changed here, not the original data!
}

void other_user_function()
{
A a;
user_function(a("1:10")); // or whatever syntax I'l use here....
}

The code 'a("1:10")' results in an object of class A which shares its
data with its 'parent' (this was coded in my original post by method
'get_shared_A' in class A, but its an operator in fact), but the copy
constructor called for 'user_function' must create an A which does not
share its data in order to provide correct behaviour.

Given the above, I don't see the solution in your suggestion...

Jeroen
Apr 24 '07 #3
* Jeroen:
Alf P. Steinbach schreef:
>* Jeroen:
>>>
The basic idea is that I have a class which has a data vector, but
objects of that class can also share that data vector between several
instances:

Use boost::shared_ptr to implement the sharing. If you want a
non-shared copy provide that via some member function. Not via the
copy constructor.

That may just be the problem. The library in which class A resides uses
the concept of shared data and so on. But for the user all of this is
not available, nor visible. The user expects 'normal' behaviour of class
A. So if a user of the library uses a subscript operator in order to
select a portion of the data in an object A and directly uses that as a
parameter to a function, then the copy constructor is called which is
required to make a non data-sharing copy:

void user_function(A a)
{
a = 10; // parameter 'a' must be changed here, not the original data!
}

void other_user_function()
{
A a;
user_function(a("1:10")); // or whatever syntax I'l use here....
}

The code 'a("1:10")' results in an object of class A which shares its
data with its 'parent' (this was coded in my original post by method
'get_shared_A' in class A, but its an operator in fact), but the copy
constructor called for 'user_function' must create an A which does not
share its data in order to provide correct behaviour.

Given the above, I don't see the solution in your suggestion...
Not to put to fine words on it, you're confused. You want a copy
constructor that has two incompatible properties: making a non-shared
copy (only), and making a shared copy (only). If the sharing, as you
write above, is an implementation detail, then try to define exactly
what it's meant to accomplish; e.g. it may be that what you're trying to
achieve is some copy-on-write scheme, in which case do as I wrote.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Apr 24 '07 #4
Alf P. Steinbach schreef:
* Jeroen:
>Alf P. Steinbach schreef:
>>* Jeroen:

The basic idea is that I have a class which has a data vector, but
objects of that class can also share that data vector between
several instances:

Use boost::shared_ptr to implement the sharing. If you want a
non-shared copy provide that via some member function. Not via the
copy constructor.

That may just be the problem. The library in which class A resides
uses the concept of shared data and so on. But for the user all of
this is not available, nor visible. The user expects 'normal'
behaviour of class A. So if a user of the library uses a subscript
operator in order to select a portion of the data in an object A and
directly uses that as a parameter to a function, then the copy
constructor is called which is required to make a non data-sharing copy:

void user_function(A a)
{
a = 10; // parameter 'a' must be changed here, not the original data!
}

void other_user_function()
{
A a;
user_function(a("1:10")); // or whatever syntax I'l use here....
}

The code 'a("1:10")' results in an object of class A which shares its
data with its 'parent' (this was coded in my original post by method
'get_shared_A' in class A, but its an operator in fact), but the copy
constructor called for 'user_function' must create an A which does not
share its data in order to provide correct behaviour.

Given the above, I don't see the solution in your suggestion...

Not to put to fine words on it, you're confused. You want a copy
constructor that has two incompatible properties: making a non-shared
copy (only), and making a shared copy (only). If the sharing, as you
write above, is an implementation detail, then try to define exactly
what it's meant to accomplish; e.g. it may be that what you're trying to
achieve is some copy-on-write scheme, in which case do as I wrote.
As I think things over, I see more problems on the horizon :-( But I
will certainly take a good look on boost::shared_ptr. Thanks for replying !
Apr 24 '07 #5
On 2007-04-24 10:49, Jeroen wrote:
Hi all,

I'm trying to implement a certain class but I have problems regarding
the copy ctor. I'll try to explain this as good as possible and show
what I tried thusfar. Because it's not about a certain code syntax but
more a 'code architecture' thing , I'll use simple example classes
(which are certainly not complete or working...) just to illustrate the
idea (and I may make some mistakes because I'm not that experienced...).

The basic idea is that I have a class which has a data vector, but
objects of that class can also share that data vector between several
instances:

class A {
vector<double*data; // pointer, so objects of 'A' can _share_ data.
bool data_is_shared; // does this 'A' share data with another A?

A(const bool do_data_init=true); // ctor
A(const A& a); // copy constructor
A get_shared_A(); // get an object A which shares 'this' data
};

Ctor:

A::A(bool do_data_init)
{
if (do_data_init)
data = new vector<double>;
data_is_shared = !do_data_init;
}

Copy ctor, always result in an 'A' which does not share data:

A::A(const A& a)
{
data = new vector<double>;
data_is_shared = false;
*data = *a.data;
}

Get an object A which shares data with 'this':

A A::get_shared_A()
{
A a(false);

a.data = data; // copy the data pointer only
return a;
}

The problems focus on this last method 'get_shared_A'. It should return
an object A that shares its data with 'this'. Problem: on returning 'a',
the copy constructor _may_ be called and then an object A is returned
which has its own data vector. So this doesn't work... I cannot change
the copy ctor because that should always return an A which does not
share its data (that's a requirement for correct behaviour of class A).

In my application, 'get_shared_A' is an operator which selects a part of
the original data in 'this', and the returned result must be an object A
because it must be used in mathematical expressions just like all other A's.

So, using a 'helper'-class B I tried to change this in:

A A::get_shared_A()
{
B b;

b.data = data; // copy the data pointer only
return b;
}

With class B:

class B {
vector<double*data;
};

And an extra conversion ctor for A:

A::A(const& B b)
{
data = b.data;
data_is_shared = true;
}

So I wanted the code to force to use this new ctor and return an object
A which shares its data. But again, after the conversion ctor is called
it is still possible that the copy ctor is also called and things do not
work correctly.

In fact, all possible solutions I came up with do not work because you
cannot be sure if the copy ctor is called when returning from
'get_shared_A', it depends on the compiler you have.

So, does anybody has any ideas to overcome this problem?
Some time a go (Qt3) I took a look at the implicit sharing mechanism of
QList, the idea is that if you do something like this:

QList a;
/* add elements to a */
QList b = a;

then b will share the elements with a until one of them makes any
changes to the list, at which point it will "detach" (which basically
means that it will create a copy of the list and make the changes to the
copy).

If I understood your problem correctly it's quite similar but the other
way around, you want explicit sharing, but the idea should still be the
same and if you understand how they did it in Qt you should be able to
make it work for you (unless you need it to be thread-safe, at which
point it becomes a bit more tricky).

--
Erik Wikström
Apr 24 '07 #6
Erik Wikström wrote:
On 2007-04-24 10:49, Jeroen wrote:
>Hi all,

I'm trying to implement a certain class but I have problems regarding
the copy ctor. I'll try to explain this as good as possible and show
what I tried thusfar. Because it's not about a certain code syntax
but more a 'code architecture' thing , I'll use simple example
classes (which are certainly not complete or working...) just to
illustrate the idea (and I may make some mistakes because I'm not
that experienced...).

The basic idea is that I have a class which has a data vector, but
objects of that class can also share that data vector between several
instances:

class A {
vector<double*data; // pointer, so objects of 'A' can _share_ data.
bool data_is_shared; // does this 'A' share data with another A?

A(const bool do_data_init=true); // ctor
A(const A& a); // copy constructor
A get_shared_A(); // get an object A which shares 'this' data
};

Ctor:

A::A(bool do_data_init)
{
if (do_data_init)
data = new vector<double>;
data_is_shared = !do_data_init;
}

Copy ctor, always result in an 'A' which does not share data:

A::A(const A& a)
{
data = new vector<double>;
data_is_shared = false;
*data = *a.data;
}

Get an object A which shares data with 'this':

A A::get_shared_A()
{
A a(false);

a.data = data; // copy the data pointer only
return a;
}

The problems focus on this last method 'get_shared_A'. It should
return an object A that shares its data with 'this'. Problem: on
returning 'a', the copy constructor _may_ be called and then an
object A is returned which has its own data vector. So this doesn't
work... I cannot change the copy ctor because that should always
return an A which does not share its data (that's a requirement for
correct behaviour of class A).

In my application, 'get_shared_A' is an operator which selects a part
of the original data in 'this', and the returned result must be an
object A because it must be used in mathematical expressions just
like all other A's.

So, using a 'helper'-class B I tried to change this in:

A A::get_shared_A()
{
B b;

b.data = data; // copy the data pointer only
return b;
}

With class B:

class B {
vector<double*data;
};

And an extra conversion ctor for A:

A::A(const& B b)
{
data = b.data;
data_is_shared = true;
}

So I wanted the code to force to use this new ctor and return an
object A which shares its data. But again, after the conversion ctor
is called it is still possible that the copy ctor is also called and
things do not work correctly.

In fact, all possible solutions I came up with do not work because
you cannot be sure if the copy ctor is called when returning from
'get_shared_A', it depends on the compiler you have.

So, does anybody has any ideas to overcome this problem?


Some time a go (Qt3) I took a look at the implicit sharing mechanism
of QList, the idea is that if you do something like this:

QList a;
/* add elements to a */
QList b = a;

then b will share the elements with a until one of them makes any
changes to the list, at which point it will "detach" (which basically
means that it will create a copy of the list and make the changes to
the copy).

If I understood your problem correctly it's quite similar but the
other way around, you want explicit sharing, but the idea should still
be the same and if you understand how they did it in Qt you should be
able to make it work for you (unless you need it to be thread-safe, at
which point it becomes a bit more tricky).
OK, thanks for the pointer :-) The problem that I came up with is a
little more 'sophisticated' than just sharing data. I try to implement a
matrix class which supports subscripting with a string, thus enabling to
'select' submatrices. For example:

matrix m(10,10); // 10x10 matrix
m("1:3,:") = 1; // rows 1 to 3 become '1'.

In this example, I use operator () for smart subscripting and this
operator must return a matrix object which only points to the selected
data range of its 'parent' matrix 'm' so it can be modified. Looks like
sharing data, but in this case I want the 'shared data' to be changed!

On the other hand, if you have a function (useless code by the way, but
just to illustrate things):

void my_function(matrix m)
{
m = 1;
}

and you call that by:

my_function(m("1:4,:"));

then my subscripting operator returns a matrix object which only points
to the selected data (this behaviour follows from the previous example),
but the copy constructor called when calling my_function must return a
matrix object which has its own data in order to prevent that the
original data is changed within my_function. But the copy ctor may
interfere within my operator () as I illustrated in my original post.

I'm at the point where I need to figure out all the functionality
required by correct matrix behaviour (the only thing I can come up with
is to generate as many possible situations like the previous examples),
and check if there isn't some inherent impossibility to make things
work.... Maybe this project is just too difficult for me :-)

Jeroen
Apr 24 '07 #7
On 2007-04-24 21:14, MathWizard wrote:
Erik Wikström wrote:
>On 2007-04-24 10:49, Jeroen wrote:
>>Hi all,

I'm trying to implement a certain class but I have problems regarding
the copy ctor. I'll try to explain this as good as possible and show
what I tried thusfar. Because it's not about a certain code syntax
but more a 'code architecture' thing , I'll use simple example
classes (which are certainly not complete or working...) just to
illustrate the idea (and I may make some mistakes because I'm not
that experienced...).

The basic idea is that I have a class which has a data vector, but
objects of that class can also share that data vector between several
instances:

class A {
vector<double*data; // pointer, so objects of 'A' can _share_ data.
bool data_is_shared; // does this 'A' share data with another A?

A(const bool do_data_init=true); // ctor
A(const A& a); // copy constructor
A get_shared_A(); // get an object A which shares 'this' data
};

Ctor:

A::A(bool do_data_init)
{
if (do_data_init)
data = new vector<double>;
data_is_shared = !do_data_init;
}

Copy ctor, always result in an 'A' which does not share data:

A::A(const A& a)
{
data = new vector<double>;
data_is_shared = false;
*data = *a.data;
}

Get an object A which shares data with 'this':

A A::get_shared_A()
{
A a(false);

a.data = data; // copy the data pointer only
return a;
}

The problems focus on this last method 'get_shared_A'. It should
return an object A that shares its data with 'this'. Problem: on
returning 'a', the copy constructor _may_ be called and then an
object A is returned which has its own data vector. So this doesn't
work... I cannot change the copy ctor because that should always
return an A which does not share its data (that's a requirement for
correct behaviour of class A).

In my application, 'get_shared_A' is an operator which selects a part
of the original data in 'this', and the returned result must be an
object A because it must be used in mathematical expressions just
like all other A's.

So, using a 'helper'-class B I tried to change this in:

A A::get_shared_A()
{
B b;

b.data = data; // copy the data pointer only
return b;
}

With class B:

class B {
vector<double*data;
};

And an extra conversion ctor for A:

A::A(const& B b)
{
data = b.data;
data_is_shared = true;
}

So I wanted the code to force to use this new ctor and return an
object A which shares its data. But again, after the conversion ctor
is called it is still possible that the copy ctor is also called and
things do not work correctly.

In fact, all possible solutions I came up with do not work because
you cannot be sure if the copy ctor is called when returning from
'get_shared_A', it depends on the compiler you have.

So, does anybody has any ideas to overcome this problem?


Some time a go (Qt3) I took a look at the implicit sharing mechanism
of QList, the idea is that if you do something like this:

QList a;
/* add elements to a */
QList b = a;

then b will share the elements with a until one of them makes any
changes to the list, at which point it will "detach" (which basically
means that it will create a copy of the list and make the changes to
the copy).

If I understood your problem correctly it's quite similar but the
other way around, you want explicit sharing, but the idea should still
be the same and if you understand how they did it in Qt you should be
able to make it work for you (unless you need it to be thread-safe, at
which point it becomes a bit more tricky).
OK, thanks for the pointer :-) The problem that I came up with is a
little more 'sophisticated' than just sharing data. I try to implement a
matrix class which supports subscripting with a string, thus enabling to
'select' submatrices. For example:

matrix m(10,10); // 10x10 matrix
m("1:3,:") = 1; // rows 1 to 3 become '1'.

In this example, I use operator () for smart subscripting and this
operator must return a matrix object which only points to the selected
data range of its 'parent' matrix 'm' so it can be modified. Looks like
sharing data, but in this case I want the 'shared data' to be changed!

On the other hand, if you have a function (useless code by the way, but
just to illustrate things):

void my_function(matrix m)
{
m = 1;
}

and you call that by:

my_function(m("1:4,:"));

then my subscripting operator returns a matrix object which only points
to the selected data (this behaviour follows from the previous example),
but the copy constructor called when calling my_function must return a
matrix object which has its own data in order to prevent that the
original data is changed within my_function. But the copy ctor may
interfere within my operator () as I illustrated in my original post.

I'm at the point where I need to figure out all the functionality
required by correct matrix behaviour (the only thing I can come up with
is to generate as many possible situations like the previous examples),
and check if there isn't some inherent impossibility to make things
work.... Maybe this project is just too difficult for me :-)
Sounds a bit like valarray and slice/slice_array, might be worth taking
a look at, if nothing else as an inspiration.

--
Erik Wikström
Apr 24 '07 #8
On 04/24/2007 02:14 PM, MathWizard wrote:
[...]
On the other hand, if you have a function (useless code by the way, but
just to illustrate things):

void my_function(matrix m)
{
m = 1;
}

and you call that by:

my_function(m("1:4,:"));

then my subscripting operator returns a matrix object which only points
to the selected data (this behaviour follows from the previous example),
but the copy constructor called when calling my_function must return a
matrix object which has its own data in order to prevent that the
original data is changed within my_function. But the copy ctor may
interfere within my operator () as I illustrated in my original post.
[...]
You might be able to eliminate the copy-constructor call by making
my_function accept a reference:

void my_function (matrix & m) { ... }

Apr 25 '07 #9
Mumia W. schreef:
On 04/24/2007 02:14 PM, MathWizard wrote:
>[...]
On the other hand, if you have a function (useless code by the way,
but just to illustrate things):

void my_function(matrix m)
{
m = 1;
}

and you call that by:

my_function(m("1:4,:"));

then my subscripting operator returns a matrix object which only
points to the selected data (this behaviour follows from the previous
example), but the copy constructor called when calling my_function
must return a matrix object which has its own data in order to prevent
that the original data is changed within my_function. But the copy
ctor may interfere within my operator () as I illustrated in my
original post.
[...]

You might be able to eliminate the copy-constructor call by making
my_function accept a reference:

void my_function (matrix & m) { ... }
That's not the problem because this works fine if I have a copy
constructor which always returns a non-sharing matrix object. The
problem lies within the operator () which I use for subscripting.
Because I do not know if a copy constructor is called on return (depends
on compiler), correct behaviour of my implementation is not guaranteed.

What I want is to differentiate between situations where a copy ctor is
called:

* if the copy ctor is called when calling a function (pass by value
parms), then a non-sharing matrix object must be created.
* if the copy ctor is called when returning a matrix object from a
function, then an exact copy must be made (sharing or non-sharing
characteristics are not changed by the copy).

That's seems to be impossible, so I think I have to come up with some
other mechanism to get it right....
Apr 25 '07 #10
On 04/25/2007 02:33 AM, Jeroen wrote:
[...]
* if the copy ctor is called when calling a function (pass by value
parms), then a non-sharing matrix object must be created.
* if the copy ctor is called when returning a matrix object from a
function, then an exact copy must be made (sharing or non-sharing
characteristics are not changed by the copy).

That's seems to be impossible, so I think I have to come up with some
other mechanism to get it right....
I think you can get closer to your objective if you're willing to return
a pointer from get_shared_A [or the overloaded operator()] rather than
an object; however, that introduces the complexities of dealing with
pointers.

Another option might be to maintain a list of sharable objects. A
modified copy constructor would find out what to do by testing if the
object it's copying is shareable:
#include <cstdio>
#include <cstdlib>
#include <cstring>

struct Vector;

struct Vector {
int len;
int * data;

static const int maxv = 100;
static int svpos;
static Vector * shareable [maxv];

Vector(int elen, int * edata) : len(elen), data(edata) { }
Vector(const Vector & obj) {
len = obj.len;
if (! obj.is_shareable()) {
data = new int[len];
memcpy(data,obj.data,len*sizeof(*data));
} else {
data = obj.data;
make_shareable();
}
}
~Vector() {
delete[] data;
len = 0; data = 0;
}
Vector get_shared() const {
Vector another(len,data);
another.make_shareable();
return another;
}
void make_shareable () {
if (svpos >= maxv) { exit(1); }
shareable[svpos++] = this;
}
bool is_shareable() const {
for (int nx = 0; nx < svpos; ++nx) {
if (shareable[nx] == this) { return true; }
}
return false;
}
void print (FILE * fid) const {
for (int nx = 0; nx < len; nx++) {
printf("%d ", data[nx]);
}
puts("");
}
};

int Vector::svpos = 0;
Vector * Vector::shareable [Vector::maxv];

int main ()
{
int itinit [] = { 6, 15, 76, 41, 91, 37, 42 };
int itsize = sizeof(itinit)/sizeof(itinit[0]);

Vector first(itsize, itinit);
Vector second = Vector(first.get_shared());

first.data[1] = 7000;
first.data[2] = 7020;

first.print(stdout);
second.print(stdout);

return EXIT_SUCCESS;
}

----------end-code--------

As you can see, I'm more of a C than a C++ programmer right now. :-)

The magic is in the copy-constructor which calls is_shareable() on the
other object before performing the copy. The list of shared objects, is
maintained as class data in this program.

My compiler, gcc, doesn't invoke the copy-ctor when I assign from
get_shared(), so I had to force it to use the copy constructor just to
be sure that the technique worked correctly.

I consider this somewhat of a kludge because the copy-ctor doesn't
always produce an independent copy. Users have to be aware that
make_shareable() might have been called on some of their objects :-(

I didn't write a make_nonshareable() for this program, but it would be
trivial to write; the primary problem is that users might not know to
call the function before attempting to make independent copies.
Apr 25 '07 #11
Mumia W. schreef:
On 04/25/2007 02:33 AM, Jeroen wrote:
>[...]
* if the copy ctor is called when calling a function (pass by value
parms), then a non-sharing matrix object must be created.
* if the copy ctor is called when returning a matrix object from a
function, then an exact copy must be made (sharing or non-sharing
characteristics are not changed by the copy).

That's seems to be impossible, so I think I have to come up with some
other mechanism to get it right....

I think you can get closer to your objective if you're willing to return
a pointer from get_shared_A [or the overloaded operator()] rather than
an object; however, that introduces the complexities of dealing with
pointers.

Another option might be to maintain a list of sharable objects. A
modified copy constructor would find out what to do by testing if the
object it's copying is shareable:
#include <cstdio>
#include <cstdlib>
#include <cstring>

struct Vector;

struct Vector {
int len;
int * data;

static const int maxv = 100;
static int svpos;
static Vector * shareable [maxv];

Vector(int elen, int * edata) : len(elen), data(edata) { }
Vector(const Vector & obj) {
len = obj.len;
if (! obj.is_shareable()) {
data = new int[len];
memcpy(data,obj.data,len*sizeof(*data));
} else {
data = obj.data;
make_shareable();
}
}
~Vector() {
delete[] data;
len = 0; data = 0;
}
Vector get_shared() const {
Vector another(len,data);
another.make_shareable();
return another;
}
void make_shareable () {
if (svpos >= maxv) { exit(1); }
shareable[svpos++] = this;
}
bool is_shareable() const {
for (int nx = 0; nx < svpos; ++nx) {
if (shareable[nx] == this) { return true; }
}
return false;
}
void print (FILE * fid) const {
for (int nx = 0; nx < len; nx++) {
printf("%d ", data[nx]);
}
puts("");
}
};

int Vector::svpos = 0;
Vector * Vector::shareable [Vector::maxv];

int main ()
{
int itinit [] = { 6, 15, 76, 41, 91, 37, 42 };
int itsize = sizeof(itinit)/sizeof(itinit[0]);

Vector first(itsize, itinit);
Vector second = Vector(first.get_shared());

first.data[1] = 7000;
first.data[2] = 7020;

first.print(stdout);
second.print(stdout);

return EXIT_SUCCESS;
}

----------end-code--------

As you can see, I'm more of a C than a C++ programmer right now. :-)

The magic is in the copy-constructor which calls is_shareable() on the
other object before performing the copy. The list of shared objects, is
maintained as class data in this program.

My compiler, gcc, doesn't invoke the copy-ctor when I assign from
get_shared(), so I had to force it to use the copy constructor just to
be sure that the technique worked correctly.

I consider this somewhat of a kludge because the copy-ctor doesn't
always produce an independent copy. Users have to be aware that
make_shareable() might have been called on some of their objects :-(

I didn't write a make_nonshareable() for this program, but it would be
trivial to write; the primary problem is that users might not know to
call the function before attempting to make independent copies.
I'll take a close look at it :-) Your observation however that "Users
have to be aware that make_shareable() might have been called on some of
their objects :-( " does not klook promising though. I try to find an
architecture which behaviour is correct in all cases... Fool-proof :-)

Thanks,

Jeroen
Apr 25 '07 #12
On Apr 24, 10:49 am, Jeroen <no_m...@thanx.comwrote:
I'm trying to implement a certain class but I have problems regarding
the copy ctor. I'll try to explain this as good as possible and show
what I tried thusfar. Because it's not about a certain code syntax but
more a 'code architecture' thing , I'll use simple example classes
(which are certainly not complete or working...) just to illustrate the
idea (and I may make some mistakes because I'm not that experienced...).
The basic idea is that I have a class which has a data vector, but
objects of that class can also share that data vector between several
instances:
class A {
vector<double*data; // pointer, so objects of 'A' can _share_ data.
bool data_is_shared; // does this 'A' share data with another A?
A(const bool do_data_init=true); // ctor
A(const A& a); // copy constructor
A get_shared_A(); // get an object A which shares 'this' data
};
Ctor:
A::A(bool do_data_init)
{
if (do_data_init)
data = new vector<double>;
data_is_shared = !do_data_init;
}
Danger: what is data if ! do_data_init?

Why not:

A::A( bool do_data_init )
: data( do_data_init ? new vector< double : NULL )
, data_is_shared( ! do_data_init )
{
}
Copy ctor, always result in an 'A' which does not share data:
A::A(const A& a)
{
data = new vector<double>;
data_is_shared = false;
*data = *a.data;
}
Why not:

A::A( A const& other )
: data( new vector<double>( *other.data )
, data_is_shared( false )
{
}

Except: hwat happens if other.data == NULL (or hasn't been
initialized, in your code)?
Get an object A which shares data with 'this':
A A::get_shared_A()
{
A a(false);
a.data = data; // copy the data pointer only
return a;
}
This won't work. The copy constructor may be used to return the
value, so you'll end up with a deep copy anyway. Sometimes,
since it's pretty much up to the compiler whether the copy
constructor is called or not.
The problems focus on this last method 'get_shared_A'. It should return
an object A that shares its data with 'this'. Problem: on returning 'a',
the copy constructor _may_ be called and then an object A is returned
which has its own data vector. So this doesn't work... I cannot change
the copy ctor because that should always return an A which does not
share its data (that's a requirement for correct behaviour of class A).
IMHO: the real solution lies in making the copy constructor
respect the sharing. Something along the lines of:

A::A( A const& other )
: data( other.data_is_shared
? other.data
: new vector< double >( *other.data ) )
, data_is_shared( other.data_is_shared )
{
}

But I still don't really like it. You have a single class,
whose semantics change from reference to copy according to an
attribute. Generally speaking, you want to treat classes with
reference semantics considerably differently than classes with
value semantics.
In my application, 'get_shared_A' is an operator which selects
a part of the original data in 'this', and the returned result
must be an object A because it must be used in mathematical
expressions just like all other A's.
In which case, it almost certainly needs value semantics. If
not, you're going to get into trouble very, very quickly.

[...]
In fact, all possible solutions I came up with do not work because you
cannot be sure if the copy ctor is called when returning from
'get_shared_A', it depends on the compiler you have.
And the level of optimization, and possibly the context in which
the function is called.
So, does anybody has any ideas to overcome this problem?
What is the problem you are trying to solve?

--
James Kanze (GABI Software) mailto:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Apr 25 '07 #13
James Kanze schreef:
On Apr 24, 10:49 am, Jeroen <no_m...@thanx.comwrote:
>I'm trying to implement a certain class but I have problems regarding
the copy ctor. I'll try to explain this as good as possible and show
what I tried thusfar. Because it's not about a certain code syntax but
more a 'code architecture' thing , I'll use simple example classes
(which are certainly not complete or working...) just to illustrate the
idea (and I may make some mistakes because I'm not that experienced...).
>The basic idea is that I have a class which has a data vector, but
objects of that class can also share that data vector between several
instances:
>class A {
vector<double*data; // pointer, so objects of 'A' can _share_ data.
bool data_is_shared; // does this 'A' share data with another A?
> A(const bool do_data_init=true); // ctor
A(const A& a); // copy constructor
A get_shared_A(); // get an object A which shares 'this' data
};
>Ctor:
>A::A(bool do_data_init)
{
if (do_data_init)
data = new vector<double>;
data_is_shared = !do_data_init;
>}

Danger: what is data if ! do_data_init?

Why not:

A::A( bool do_data_init )
: data( do_data_init ? new vector< double : NULL )
, data_is_shared( ! do_data_init )
{
}
It looks a bit clumsy the way I put this example code down, but only in
a single method (get_shared_A() see a few lines lateron) the parameter
do_data_init should get the value 'false'. In that method I take care of
property data. The user of the library should not know (is not
allowed) to use the construction with do_data_init=false.
>Copy ctor, always result in an 'A' which does not share data:
>A::A(const A& a)
{
data = new vector<double>;
data_is_shared = false;
*data = *a.data;
}

Why not:

A::A( A const& other )
: data( new vector<double>( *other.data )
, data_is_shared( false )
{
}

Except: hwat happens if other.data == NULL (or hasn't been
initialized, in your code)?
Should be taken care of because get_shared_A is the only function that
is allowed to call the ctor withoud initializing 'data'.
>Get an object A which shares data with 'this':
>A A::get_shared_A()
{
A a(false);
> a.data = data; // copy the data pointer only
return a;
}

This won't work. The copy constructor may be used to return the
value, so you'll end up with a deep copy anyway. Sometimes,
since it's pretty much up to the compiler whether the copy
constructor is called or not.
Yep, I figured it out, see below...
>
>The problems focus on this last method 'get_shared_A'. It should return
an object A that shares its data with 'this'. Problem: on returning 'a',
the copy constructor _may_ be called and then an object A is returned
which has its own data vector. So this doesn't work... I cannot change
the copy ctor because that should always return an A which does not
share its data (that's a requirement for correct behaviour of class A).

IMHO: the real solution lies in making the copy constructor
respect the sharing. Something along the lines of:

A::A( A const& other )
: data( other.data_is_shared
? other.data
: new vector< double >( *other.data ) )
, data_is_shared( other.data_is_shared )
{
}

But I still don't really like it. You have a single class,
whose semantics change from reference to copy according to an
attribute. Generally speaking, you want to treat classes with
reference semantics considerably differently than classes with
value semantics.
Uop till now, yes... I thought this was the way to go for my application...
>In my application, 'get_shared_A' is an operator which selects
a part of the original data in 'this', and the returned result
must be an object A because it must be used in mathematical
expressions just like all other A's.

In which case, it almost certainly needs value semantics. If
not, you're going to get into trouble very, very quickly.

[...]
>In fact, all possible solutions I came up with do not work because you
cannot be sure if the copy ctor is called when returning from
'get_shared_A', it depends on the compiler you have.

And the level of optimization, and possibly the context in which
the function is called.
>So, does anybody has any ideas to overcome this problem?

What is the problem you are trying to solve?
I write a post under my home-account 'MathWizard' in this thread. Maybe
that gives a little more explanation of what I try to achieve. As I
mention in that post, it may be impossible to find a solution for the
things I want to implement and I have to do a lot of thinking about that.

Again, thanks for your time and effort James. It's very helpfull to me.

Jeroen
>
--
James Kanze (GABI Software) mailto:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Apr 26 '07 #14

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

1
2522
by: Tobias Langner | last post by:
I try to program a smart-pointer using policies. I rely heavyly on templates during this. The program compiles fine - but I get a linker error: test.o(.text+0x3e): In function `testSmartPtr()':...
42
5721
by: Edward Diener | last post by:
Coming from the C++ world I can not understand the reason why copy constructors are not used in the .NET framework. A copy constructor creates an object from a copy of another object of the same...
22
1769
by: San | last post by:
Hi, I wrote two simple constructors (one of them default) to initialize a student object. Student::Student(string name) { student_name = name; student_id = LATEST_ID + 1;...
4
1416
by: Apricot | last post by:
#include <iostream> #include <string> #include <map> using namespace std ; class tst { public : tst() { cout << "tst::constructor" << endl ; } tst(const tst & that) { cout << "tst::copy...
2
1790
by: John Ratliff | last post by:
What are the ramifications of creating a private copy constructor? Say I didn't want any copies to be created, say if I had a singleton for example. Should I make the copy constructor public...
3
2090
by: Tony Johansson | last post by:
Hello experts! I have this piece of code. No user defined copy constructor exist. AccountForStudent create(long number) { AccountForStudent local(number, 0.0); return local; } int main() {
11
3407
by: Nindi73 | last post by:
A few days a ago I posted my code for a deep copy pointer which doesn't require the pointee object to have a virtual copy constructor. I need help with checking that it was exception safe and...
1
2106
by: blangela | last post by:
3.0 Advanced Topic Addendum There are a few cases where the C++ compiler cannot provide an overloaded assignment operator for your class. If your class contains a const member or/and a...
19
2360
by: Jeroen | last post by:
Hi guys, I have a simple question. If I have a class like: class A { A(); ~A(); A(A& a); A(int i);
0
7237
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
7137
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
7416
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
1
7073
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...
0
5656
agi2029
by: agi2029 | last post by:
Let's talk about the concept of autonomous AI software engineers and no-code agents. These AIs are designed to manage the entire lifecycle of a software development project—planning, coding, testing,...
1
5062
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...
0
3218
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The...
0
1571
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated ...
0
443
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.