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

Discussion in 'C++' started by Jeroen, Apr 24, 2007.

  1. Jeroen

    Jeroen Guest

    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
    Jeroen, Apr 24, 2007
    #1
    1. Advertising

  2. * 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?
    Alf P. Steinbach, Apr 24, 2007
    #2
    1. Advertising

  3. Jeroen

    Jeroen Guest

    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
    Jeroen, Apr 24, 2007
    #3
  4. * 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?
    Alf P. Steinbach, Apr 24, 2007
    #4
  5. Jeroen

    Jeroen Guest

    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 !
    Jeroen, Apr 24, 2007
    #5
  6. 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
    =?ISO-8859-1?Q?Erik_Wikstr=F6m?=, Apr 24, 2007
    #6
  7. Jeroen

    MathWizard Guest

    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
    MathWizard, Apr 24, 2007
    #7
  8. 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
    =?ISO-8859-1?Q?Erik_Wikstr=F6m?=, Apr 24, 2007
    #8
  9. Jeroen

    Mumia W. Guest

    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) { ... }
    Mumia W., Apr 25, 2007
    #9
  10. Jeroen

    Jeroen Guest

    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....
    Jeroen, Apr 25, 2007
    #10
  11. Jeroen

    Mumia W. Guest

    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.
    Mumia W., Apr 25, 2007
    #11
  12. Jeroen

    Jeroen Guest

    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
    Jeroen, Apr 25, 2007
    #12
  13. Jeroen

    James Kanze Guest

    On Apr 24, 10:49 am, Jeroen <> wrote:

    > 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:
    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
    James Kanze, Apr 25, 2007
    #13
  14. Jeroen

    Jeroen Guest

    James Kanze schreef:
    > On Apr 24, 10:49 am, Jeroen <> wrote:
    >
    >> 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:
    > 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
    >
    Jeroen, Apr 26, 2007
    #14
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Wes Harrison
    Replies:
    2
    Views:
    409
    Wes Harrison
    Jul 6, 2005
  2. jupiter

    Long story

    jupiter, Dec 28, 2006, in forum: Java
    Replies:
    1
    Views:
    330
    Daniel Pitts
    Dec 28, 2006
  3. Generic Usenet Account
    Replies:
    10
    Views:
    2,197
  4. Replies:
    4
    Views:
    433
    James Kanze
    Mar 19, 2009
  5. cinsk
    Replies:
    35
    Views:
    2,559
    James Kanze
    Oct 11, 2010
Loading...

Share This Page