How to use reference to do cast operator?

Discussion in 'C++' started by Immortal Nephi, Jul 19, 2010.

  1. I write two different classes. Two classes have their own memory
    that holds data in array. They can have different algorithms to
    manipulate data.
    I would like to write my code to transfer data between two classes.
    First class has tasks to process data before it can transfer data to
    second class. After the data transfer is complete, both classes use
    their own algorithms to manipulate data.
    My code looks like that:

    int main() {
    Class_A a;
    Class_B b;

    a.Set_1( 0, 0x41 ).Set_2( 0, 0x42 ).Set_3( 0, 0x43 );
    a.Set_1( 1, 0x44 ).Set_2( 1, 0x45 ).Set_3( 1, 0x46 );
    a.Set_1( 2, 0x47 ).Set_2( 2, 0x48 ).Set_3( 2, 0x49 );
    a.Set_1( 3, 0x4a ).Set_2( 3, 0x4b ).Set_3( 3, 0x4c );

    b.Set_1( 0, 0x71 ).Set_2( 0, 0x72 ).Set_3( 0, 0x73 );
    b.Set_1( 1, 0x74 ).Set_2( 1, 0x75 ).Set_3( 1, 0x76 );
    b.Set_1( 2, 0x77 ).Set_2( 2, 0x78 ).Set_3( 2, 0x79 );
    b.Set_1( 3, 0x7a ).Set_2( 3, 0x7b ).Set_3( 3, 0x7c );

    for( int x = 0; x < 4; x++ )
    a.Set_1( x, b.Get_1( x ) ).Set_2( x, b.Get_2( x ) ).Set_3( x,
    b.Get_3( x ) );

    return 0;
    }

    Do you notice for loop? For loop transfers data from class b to
    class a. I decide to replace from for loop to cast operator. You can
    write that code like this.

    // for( int x = 0; x < 4; x++ )
    // a.Set_1( x, b.Get_1( x ) ).Set_2( x, b.Get_2( x ) ).Set_3( x,
    b.Get_3( x ) );

    a = b; // class b is converted to class a by moving data

    The cast operator function looks like:

    Class_B::eek:perator Class_A () {
    Class_A a;

    for( int x = 0; x < 4; x++ )
    a.Set_1( x, m_Data_1[ x ] ).Set_2( x, m_Data_2[ x ] ).Set_3( x,
    m_Data_3[ x ] );

    return a;
    }

    It is possible that transferring data between memory can cause
    overhead because cast operator creates temporary class a in memory
    before data transfer begins. After cast operator function terminates,
    temporary class a is created second time because it uses copy
    constructor.
    After copy constructor is completed, temporary class a is
    deallocated. Return to the main() function, temporary class a for
    copy constructor is deallocated.
    You can see that data transfer requires temporary class a twice. How
    do you use reference? Reference can avoid reallocate memory twice.
    Sometimes, I want to use operator[] to select element in the array in
    class b and transfers it to class a rather than copying all data in
    memory.

    for( int x = 0; x < 4; x++ )
    a[ x ] = b[ x ]; // use operator [] to do cast operator

    // a = b; // All elements from class b is transferred to class a
    without using operator[]

    I can’t think how to add operator[] to both class a and class b.
    Maybe, proxy class is needed. I tried to write operator[] function
    but it did not work.

    You can examine complete code below if you wish.

    class Class_A;
    class Class_B;

    class Class_A {
    public:
    Class_A();
    Class_A( const Class_A &class_a );
    ~Class_A();

    Class_A &Set_1( int index, unsigned char value );
    Class_A &Set_2( int index, unsigned char value );
    Class_A &Set_3( int index, unsigned char value );

    unsigned char Get_1( int index );
    unsigned char Get_2( int index );
    unsigned char Get_3( int index );

    operator Class_B ();

    private:
    unsigned char m_Data_1[ 4 ];
    unsigned char m_Data_2[ 4 ];
    unsigned char m_Data_3[ 4 ];
    };

    class Class_B {
    public:
    Class_B();
    Class_B( const Class_B &class_b );
    ~Class_B();

    Class_B &Set_1( int index, unsigned char value );
    Class_B &Set_2( int index, unsigned char value );
    Class_B &Set_3( int index, unsigned char value );

    unsigned char Get_1( int index );
    unsigned char Get_2( int index );
    unsigned char Get_3( int index );

    operator Class_A ();

    private:
    unsigned char m_Data_1[ 4 ];
    unsigned char m_Data_2[ 4 ];
    unsigned char m_Data_3[ 4 ];
    };

    Class_A::Class_A() {
    for( int x = 0; x < 4; x++ )
    m_Data_1[ x ] = m_Data_2[ x ] = m_Data_3[ x ] = 0;
    }

    Class_A::Class_A( const Class_A &class_a ) {
    for( int x = 0; x < 4; x++ ) {
    m_Data_1[ x ] = class_a.m_Data_1[ x ];
    m_Data_2[ x ] = class_a.m_Data_2[ x ];
    m_Data_3[ x ] = class_a.m_Data_3[ x ];
    }
    }

    Class_A::~Class_A() {
    }

    Class_A &Class_A::Set_1( int index, unsigned char value ) {
    m_Data_1[ index ] = value;
    return *this;
    }

    Class_A &Class_A::Set_2( int index, unsigned char value ) {
    m_Data_2[ index ] = value;
    return *this;
    }

    Class_A &Class_A::Set_3( int index, unsigned char value ) {
    m_Data_3[ index ] = value;
    return *this;
    }

    unsigned char Class_A::Get_1( int index ) {
    return m_Data_1[ index ];
    }

    unsigned char Class_A::Get_2( int index ) {
    return m_Data_2[ index ];
    }

    unsigned char Class_A::Get_3( int index ) {
    return m_Data_3[ index ];
    }

    Class_A::eek:perator Class_B () {
    Class_B b;

    for( int x = 0; x < 4; x++ )
    b.Set_1( x, m_Data_1[ x ] ).Set_2( x, m_Data_2[ x ] ).Set_3( x,
    m_Data_3[ x ] );

    return b;
    }


    Class_B::Class_B() {
    for( int x = 0; x < 4; x++ )
    m_Data_1[ x ] = m_Data_2[ x ] = m_Data_3[ x ] = 0;
    }

    Class_B::Class_B( const Class_B &class_b ) {
    for( int x = 0; x < 4; x++ ) {
    m_Data_1[ x ] = class_b.m_Data_1[ x ];
    m_Data_2[ x ] = class_b.m_Data_2[ x ];
    m_Data_3[ x ] = class_b.m_Data_3[ x ];
    }
    }

    Class_B::~Class_B() {
    }

    Class_B &Class_B::Set_1( int index, unsigned char value ) {
    m_Data_1[ index ] = value;
    return *this;
    }

    Class_B &Class_B::Set_2( int index, unsigned char value ) {
    m_Data_2[ index ] = value;
    return *this;
    }

    Class_B &Class_B::Set_3( int index, unsigned char value ) {
    m_Data_3[ index ] = value;
    return *this;
    }

    unsigned char Class_B::Get_1( int index ) {
    return m_Data_1[ index ];
    }

    unsigned char Class_B::Get_2( int index ) {
    return m_Data_2[ index ];
    }

    unsigned char Class_B::Get_3( int index ) {
    return m_Data_3[ index ];
    }

    Class_B::eek:perator Class_A () {
    Class_A a;

    for( int x = 0; x < 4; x++ )
    a.Set_1( x, m_Data_1[ x ] ).Set_2( x, m_Data_2[ x ] ).Set_3( x,
    m_Data_3[ x ] );

    return a;
    }


    int main() {
    Class_A a;
    Class_B b;

    a.Set_1( 0, 0x41 ).Set_2( 0, 0x42 ).Set_3( 0, 0x43 );
    a.Set_1( 1, 0x44 ).Set_2( 1, 0x45 ).Set_3( 1, 0x46 );
    a.Set_1( 2, 0x47 ).Set_2( 2, 0x48 ).Set_3( 2, 0x49 );
    a.Set_1( 3, 0x4a ).Set_2( 3, 0x4b ).Set_3( 3, 0x4c );

    b.Set_1( 0, 0x71 ).Set_2( 0, 0x72 ).Set_3( 0, 0x73 );
    b.Set_1( 1, 0x74 ).Set_2( 1, 0x75 ).Set_3( 1, 0x76 );
    b.Set_1( 2, 0x77 ).Set_2( 2, 0x78 ).Set_3( 2, 0x79 );
    b.Set_1( 3, 0x7a ).Set_2( 3, 0x7b ).Set_3( 3, 0x7c );

    // First method
    for( int x = 0; x < 4; x++ )
    a.Set_1( x, b.Get_1( x ) ).Set_2( x, b.Get_2( x ) ).Set_3( x,
    b.Get_3( x ) );

    // Second method
    a = b;

    // Third method
    for( int x = 0; x < 4; x++ )
    a[ x ] = b[ x ];

    return 0;
    }
    Immortal Nephi, Jul 19, 2010
    #1
    1. Advertising

  2. Immortal Nephi <>, on 18/07/2010 18:20:18, wrote:

    > I write two different classes. Two classes have their own memory
    > that holds data in array. They can have different algorithms to
    > manipulate data.
    > I would like to write my code to transfer data between two classes.
    > First class has tasks to process data before it can transfer data to
    > second class. After the data transfer is complete, both classes use
    > their own algorithms to manipulate data.



    I see two steps here above:

    - process the data in the first class (and I would do that either in the
    construction step or in the first call to the getter function)

    - transfer the data to the second class (and I would do it with an
    assignment operator that takes a const reference to the first class)

    Implementing the second step as an assignment operator has the advantage
    of not having to worry about temporaries, assigning directly to the
    private data of the class that is being assigned to.


    > My code looks like that:
    >
    > int main() {
    > Class_A a;
    > Class_B b;
    >
    > a.Set_1( 0, 0x41 ).Set_2( 0, 0x42 ).Set_3( 0, 0x43 );
    > a.Set_1( 1, 0x44 ).Set_2( 1, 0x45 ).Set_3( 1, 0x46 );
    > a.Set_1( 2, 0x47 ).Set_2( 2, 0x48 ).Set_3( 2, 0x49 );
    > a.Set_1( 3, 0x4a ).Set_2( 3, 0x4b ).Set_3( 3, 0x4c );
    >
    > b.Set_1( 0, 0x71 ).Set_2( 0, 0x72 ).Set_3( 0, 0x73 );
    > b.Set_1( 1, 0x74 ).Set_2( 1, 0x75 ).Set_3( 1, 0x76 );
    > b.Set_1( 2, 0x77 ).Set_2( 2, 0x78 ).Set_3( 2, 0x79 );
    > b.Set_1( 3, 0x7a ).Set_2( 3, 0x7b ).Set_3( 3, 0x7c );
    >
    > for( int x = 0; x< 4; x++ )
    > a.Set_1( x, b.Get_1( x ) ).Set_2( x, b.Get_2( x ) ).Set_3( x,
    > b.Get_3( x ) );
    >
    > return 0;
    > }
    >
    > Do you notice for loop? For loop transfers data from class b to
    > class a. I decide to replace from for loop to cast operator. You can
    > write that code like this.
    >
    > // for( int x = 0; x< 4; x++ )
    > // a.Set_1( x, b.Get_1( x ) ).Set_2( x, b.Get_2( x ) ).Set_3( x,
    > b.Get_3( x ) );
    >
    > a = b; // class b is converted to class a by moving data
    >
    > The cast operator function looks like:
    >
    > Class_B::eek:perator Class_A () {
    > Class_A a;
    >
    > for( int x = 0; x< 4; x++ )
    > a.Set_1( x, m_Data_1[ x ] ).Set_2( x, m_Data_2[ x ] ).Set_3( x,
    > m_Data_3[ x ] );
    >
    > return a;
    > }
    >
    > It is possible that transferring data between memory can cause
    > overhead because cast operator creates temporary class a in memory
    > before data transfer begins. After cast operator function terminates,
    > temporary class a is created second time because it uses copy
    > constructor.



    Temporaries may be optimized out by the compiler, depending on the
    cases. I don't think you really need a conversion operator (the one you
    call "cast" operator) - the assignment operator I mentioned above should
    be more efficient - unless, maybe, if the class needs special care in
    case of self-assignment, which seems not to be your case.


    > After copy constructor is completed, temporary class a is
    > deallocated. Return to the main() function, temporary class a for
    > copy constructor is deallocated.
    > You can see that data transfer requires temporary class a twice. How
    > do you use reference? Reference can avoid reallocate memory twice.
    > Sometimes, I want to use operator[] to select element in the array in
    > class b and transfers it to class a rather than copying all data in
    > memory.
    >
    > for( int x = 0; x< 4; x++ )
    > a[ x ] = b[ x ]; // use operator [] to do cast operator
    >
    > // a = b; // All elements from class b is transferred to class a
    > without using operator[]
    >
    > I can’t think how to add operator[] to both class a and class b.
    > Maybe, proxy class is needed. I tried to write operator[] function
    > but it did not work.
    >



    You didn't post your operator[], which was the problem? I don't think
    there should be any problem in implementing it for both of your classes.


    --
    FSC - http://userscripts.org/scripts/show/59948
    http://fscode.altervista.org - http://sardinias.com
    Francesco S. Carta, Jul 19, 2010
    #2
    1. Advertising

  3. On Jul 18, 9:13 pm, "Francesco S. Carta" <> wrote:
    > Immortal Nephi <>, on 18/07/2010 18:20:18, wrote:
    >
    > >    I write two different classes.  Two classes have their own memory
    > > that holds data in array.  They can have different algorithms to
    > > manipulate data.
    > >    I would like to write my code to transfer data between two classes.
    > > First class has tasks to process data before it can transfer data to
    > > second class.  After the data transfer is complete, both classes use
    > > their own algorithms to manipulate data.

    >
    > I see two steps here above:
    >
    > - process the data in the first class (and I would do that either in the
    > construction step or in the first call to the getter function)
    >
    > - transfer the data to the second class (and I would do it with an
    > assignment operator that takes a const reference to the first class)
    >
    > Implementing the second step as an assignment operator has the advantage
    > of not having to worry about temporaries, assigning directly to the
    > private data of the class that is being assigned to.
    >
    >
    >
    >
    >
    > >    My code looks like that:

    >
    > > int main() {
    > >    Class_A a;
    > >    Class_B b;

    >
    > >    a.Set_1( 0, 0x41 ).Set_2( 0, 0x42 ).Set_3( 0, 0x43 );
    > >    a.Set_1( 1, 0x44 ).Set_2( 1, 0x45 ).Set_3( 1, 0x46 );
    > >    a.Set_1( 2, 0x47 ).Set_2( 2, 0x48 ).Set_3( 2, 0x49 );
    > >    a.Set_1( 3, 0x4a ).Set_2( 3, 0x4b ).Set_3( 3, 0x4c );

    >
    > >    b.Set_1( 0, 0x71 ).Set_2( 0, 0x72 ).Set_3( 0, 0x73 );
    > >    b.Set_1( 1, 0x74 ).Set_2( 1, 0x75 ).Set_3( 1, 0x76 );
    > >    b.Set_1( 2, 0x77 ).Set_2( 2, 0x78 ).Set_3( 2, 0x79 );
    > >    b.Set_1( 3, 0x7a ).Set_2( 3, 0x7b ).Set_3( 3, 0x7c );

    >
    > >    for( int x = 0; x<  4; x++ )
    > >            a.Set_1( x, b.Get_1( x ) ).Set_2( x, b.Get_2( x ) ).Set_3( x,
    > > b.Get_3( x ) );

    >
    > >    return 0;
    > > }

    >
    > >    Do you notice for loop?  For loop transfers data from class b to
    > > class a.  I decide to replace from for loop to cast operator.  You can
    > > write that code like this.

    >
    > > // for( int x = 0; x<  4; x++ )
    > > //         a.Set_1( x, b.Get_1( x ) ).Set_2( x, b.Get_2( x ) ).Set_3( x,
    > > b.Get_3( x ) );

    >
    > >    a = b; // class b is converted to class a by moving data

    >
    > >    The cast operator function looks like:

    >
    > > Class_B::eek:perator Class_A () {
    > >    Class_A a;

    >
    > >    for( int x = 0; x<  4; x++ )
    > >            a.Set_1( x, m_Data_1[ x ] ).Set_2( x, m_Data_2[ x ] ).Set_3( x,
    > > m_Data_3[ x ] );

    >
    > >    return a;
    > > }

    >
    > >    It is possible that transferring data between memory can cause
    > > overhead because cast operator creates temporary class a in memory
    > > before data transfer begins.  After cast operator function terminates,
    > > temporary class a is created second time because it uses copy
    > > constructor.

    >
    > Temporaries may be optimized out by the compiler, depending on the
    > cases. I don't think you really need a conversion operator (the one you
    > call "cast" operator) - the assignment operator I mentioned above should
    > be more efficient - unless, maybe, if the class needs special care in
    > case of self-assignment, which seems not to be your case.
    >
    >
    >
    >
    >
    > >    After copy constructor is completed, temporary class a is
    > > deallocated.  Return to the main() function, temporary class a for
    > > copy constructor is deallocated.
    > >    You can see that data transfer requires temporary class a twice.  How
    > > do you use reference?  Reference can avoid reallocate memory twice.
    > >    Sometimes, I want to use operator[] to select element in the array in
    > > class b and transfers it to class a rather than copying all data in
    > > memory.

    >
    > >    for( int x = 0; x<  4; x++ )
    > >            a[ x ] = b[ x ]; // use operator [] to do cast operator

    >
    > > // a = b; // All elements from class b is transferred to class a
    > > without using operator[]

    >
    > >    I can’t think how to add operator[] to both class a and class b.
    > > Maybe, proxy class is needed.  I tried to write operator[] function
    > > but it did not work.

    >
    > You didn't post your operator[], which was the problem? I don't think
    > there should be any problem in implementing it for both of your classes.
    >


    You explain very well how operator= works. I add operator[] to both
    class A and class B. I add reference on both classes to the return
    type.
    After operator[] receives index, it copies index into data member and
    then returns class A reference.
    You may find out the trick. Please look at my code. Let me know
    what you think?

    class Class_A;
    class Class_B;

    class Class_A {
    friend Class_B;
    public:
    Class_A();
    Class_A( const Class_A &class_a );
    ~Class_A();

    Class_A &Set_1( int index, unsigned char value );
    Class_A &Set_2( int index, unsigned char value );
    Class_A &Set_3( int index, unsigned char value );

    unsigned char Get_1( int index );
    unsigned char Get_2( int index );
    unsigned char Get_3( int index );

    Class_A &operator=( Class_B &class_b );
    Class_A &operator[]( int index );

    private:
    unsigned char m_Data_1[ 4 ];
    unsigned char m_Data_2[ 4 ];
    unsigned char m_Data_3[ 4 ];
    int m_Index;
    bool m_All;
    };

    class Class_B {
    friend Class_A;
    public:
    Class_B();
    Class_B( const Class_B &class_b );
    ~Class_B();

    Class_B &Set_1( int index, unsigned char value );
    Class_B &Set_2( int index, unsigned char value );
    Class_B &Set_3( int index, unsigned char value );

    unsigned char Get_1( int index );
    unsigned char Get_2( int index );
    unsigned char Get_3( int index );

    Class_B &operator=( Class_A &class_a );
    Class_B &operator[]( int index );

    private:
    unsigned char m_Data_1[ 4 ];
    unsigned char m_Data_2[ 4 ];
    unsigned char m_Data_3[ 4 ];
    int m_Index;
    bool m_All;
    };

    Class_A::Class_A() {
    for( int x = 0; x < 4; x++ )
    m_Data_1[ x ] = m_Data_2[ x ] = m_Data_3[ x ] = 0;

    m_Index = 0;
    m_All = true;
    }

    Class_A::Class_A( const Class_A &class_a ) {
    for( int x = 0; x < 4; x++ ) {
    m_Data_1[ x ] = class_a.m_Data_1[ x ];
    m_Data_2[ x ] = class_a.m_Data_2[ x ];
    m_Data_3[ x ] = class_a.m_Data_3[ x ];
    }
    }

    Class_A::~Class_A() {
    }

    Class_A &Class_A::Set_1( int index, unsigned char value ) {
    m_Data_1[ index ] = value;
    return *this;
    }

    Class_A &Class_A::Set_2( int index, unsigned char value ) {
    m_Data_2[ index ] = value;
    return *this;
    }

    Class_A &Class_A::Set_3( int index, unsigned char value ) {
    m_Data_3[ index ] = value;
    return *this;
    }

    unsigned char Class_A::Get_1( int index ) {
    return m_Data_1[ index ];
    }

    unsigned char Class_A::Get_2( int index ) {
    return m_Data_2[ index ];
    }

    unsigned char Class_A::Get_3( int index ) {
    return m_Data_3[ index ];
    }

    Class_A &Class_A::eek:perator=( Class_B &class_b ) {
    if( m_All == class_b.m_All ) {
    if( class_b.m_All == true ) {
    for( int x = 0; x < 4; x++ ) {
    m_Data_1[ x ] = class_b.m_Data_1[ x ];
    m_Data_2[ x ] = class_b.m_Data_2[ x ];
    m_Data_3[ x ] = class_b.m_Data_3[ x ];
    }
    }
    else {
    m_Data_1[ m_Index ] = class_b.m_Data_1[ class_b.m_Index ];
    m_Data_2[ m_Index ] = class_b.m_Data_2[ class_b.m_Index ];
    m_Data_3[ m_Index ] = class_b.m_Data_3[ class_b.m_Index ];

    m_All = class_b.m_All = true;
    }
    }
    else {
    // Do nothing -- invalid example
    // Class_A[ 1 ] = Class_B
    // Class_A = Class_B[ 2 ]
    m_All = class_b.m_All = true;
    }

    return *this;
    }

    Class_A &Class_A::eek:perator[]( int index ) {
    m_Index = index;
    m_All = false;
    return *this;
    }

    Class_B::Class_B() {
    for( int x = 0; x < 4; x++ )
    m_Data_1[ x ] = m_Data_2[ x ] = m_Data_3[ x ] = 0;

    m_Index = 0;
    m_All = true;
    }

    Class_B::Class_B( const Class_B &class_b ) {
    for( int x = 0; x < 4; x++ ) {
    m_Data_1[ x ] = class_b.m_Data_1[ x ];
    m_Data_2[ x ] = class_b.m_Data_2[ x ];
    m_Data_3[ x ] = class_b.m_Data_3[ x ];
    }
    }

    Class_B::~Class_B() {
    }

    Class_B &Class_B::Set_1( int index, unsigned char value ) {
    m_Data_1[ index ] = value;
    return *this;
    }

    Class_B &Class_B::Set_2( int index, unsigned char value ) {
    m_Data_2[ index ] = value;
    return *this;
    }

    Class_B &Class_B::Set_3( int index, unsigned char value ) {
    m_Data_3[ index ] = value;
    return *this;
    }

    unsigned char Class_B::Get_1( int index ) {
    return m_Data_1[ index ];
    }

    unsigned char Class_B::Get_2( int index ) {
    return m_Data_2[ index ];
    }

    unsigned char Class_B::Get_3( int index ) {
    return m_Data_3[ index ];
    }

    Class_B &Class_B::eek:perator=( Class_A &class_a ) {
    if( m_All == class_a.m_All ) {
    if( class_a.m_All == true ) {
    for( int x = 0; x < 4; x++ ) {
    m_Data_1[ x ] = class_a.m_Data_1[ x ];
    m_Data_2[ x ] = class_a.m_Data_2[ x ];
    m_Data_3[ x ] = class_a.m_Data_3[ x ];
    }
    }
    else {
    m_Data_1[ m_Index ] = class_a.m_Data_1[ class_a.m_Index ];
    m_Data_2[ m_Index ] = class_a.m_Data_2[ class_a.m_Index ];
    m_Data_3[ m_Index ] = class_a.m_Data_3[ class_a.m_Index ];

    m_All = class_a.m_All = true;
    }
    }
    else {
    m_All = class_a.m_All = true;
    // Do nothing -- invalid example
    // Class_A[ 1 ] = Class_B
    // Class_A = Class_B[ 2 ]
    }

    return *this;
    }

    Class_B &Class_B::eek:perator[]( int index ) {
    m_Index = index;
    m_All = false;
    return *this;
    }

    int main() {
    Class_A a;
    Class_B b;

    a.Set_1( 0, 0x41 ).Set_2( 0, 0x42 ).Set_3( 0, 0x43 );
    a.Set_1( 1, 0x44 ).Set_2( 1, 0x45 ).Set_3( 1, 0x46 );
    a.Set_1( 2, 0x47 ).Set_2( 2, 0x48 ).Set_3( 2, 0x49 );
    a.Set_1( 3, 0x4a ).Set_2( 3, 0x4b ).Set_3( 3, 0x4c );

    b.Set_1( 0, 0x71 ).Set_2( 0, 0x72 ).Set_3( 0, 0x73 );
    b.Set_1( 1, 0x74 ).Set_2( 1, 0x75 ).Set_3( 1, 0x76 );
    b.Set_1( 2, 0x77 ).Set_2( 2, 0x78 ).Set_3( 2, 0x79 );
    b.Set_1( 3, 0x7a ).Set_2( 3, 0x7b ).Set_3( 3, 0x7c );

    a[ 1 ] = b[ 2 ];
    a = b;

    // Do nothing -- invalid example
    a[ 1 ] = b;
    a = b[ 2 ];
    b[ 3 ] = a;

    return 0;
    }
    Immortal Nephi, Jul 20, 2010
    #3
  4. Immortal Nephi <>, on 20/07/2010 14:45:22, wrote:

    > On Jul 18, 9:13 pm, "Francesco S. Carta"<> wrote:
    >> Immortal Nephi<>, on 18/07/2010 18:20:18, wrote:


    <large snip>

    >>> I can’t think how to add operator[] to both class a and class b.
    >>> Maybe, proxy class is needed. I tried to write operator[] function
    >>> but it did not work.

    >>
    >> You didn't post your operator[], which was the problem? I don't think
    >> there should be any problem in implementing it for both of your classes.
    >>

    >
    > You explain very well how operator= works. I add operator[] to both
    > class A and class B. I add reference on both classes to the return
    > type.
    > After operator[] receives index, it copies index into data member and
    > then returns class A reference.
    > You may find out the trick. Please look at my code. Let me know
    > what you think?


    <snip code>

    I see the trick you implemented, it can be a good idea but you did not
    implement it very well.

    You left room for a lot of nonsensical instructions which lead to either
    no effect or misleading effect.

    If an operation is not supported, you have to stop it at compile time,
    so that the client code understands it's doing something that is not
    expected to work - if your interface "seems" to support an operation but
    in fact does just a no-op, that's just bad design.

    If an operation can not be executed with some inappropriate input, you
    have to intercept that wrong input and either throw an exception or do a
    no-op returning some kind of error flag.

    As it is, your code allows these bad instructions:

    a.Set_1(42, 10);

    The above is bad because writes out of the array boundaries, leading to
    a wide series of well known problems. Always check the input before
    feeding it as an array index.

    a[1][2] = b[1][2];

    The above is bad because only the second series gets copied, while
    somebody could think that also the first series will - and it won't.

    The solution to the above problem could be to /not/ return a Class_X&
    from the subscript operator, but instead some different proxy class that
    ensure the appropriate number and sequence of subscript operators - I
    wouldn't consider implementing such proxies as a really easy task, but
    maybe that's just due to my limited experience about this subject.

    There is another issue with your class(es): duplicate code - a problem
    into which I happen to fall quite often when starting a new program.

    If those classes share almost all the interface (and the underlying
    data) all of this should be done with some common base class.

    (if it were my code, I would also get rid of the duplicate code that
    deals with those three arrays by putting them together in a further
    array, accessing them by index instead that by different names as you
    do... but if it were my code I would also use vectors instead of arrays)

    The mixed uppercase/lowercase/underscore naming style is quite weird to
    my eyes, but this last issue is just matter of style/tastes.

    Well, you asked for my opinion, sorry if it seems too bad as a
    dissection, I learnt to take the good things from such kind of replies,
    when somebody else dissected my code - I hope you'll be able to do the
    same with my reply.

    Have nice time coding with C++

    --
    FSC - http://userscripts.org/scripts/show/59948
    http://fscode.altervista.org - http://sardinias.com
    Francesco S. Carta, Jul 21, 2010
    #4
  5. On Jul 20, 6:24 pm, "Francesco S. Carta" <> wrote:
    > Immortal Nephi <>, on 20/07/2010 14:45:22, wrote:
    >
    > > On Jul 18, 9:13 pm, "Francesco S. Carta"<>  wrote:
    > >> Immortal Nephi<>, on 18/07/2010 18:20:18, wrote:

    >
    > <large snip>
    >
    > >>>     I can’t think how to add operator[] to both class a and class b.
    > >>> Maybe, proxy class is needed.  I tried to write operator[] function
    > >>> but it did not work.

    >
    > >> You didn't post your operator[], which was the problem? I don't think
    > >> there should be any problem in implementing it for both of your classes.

    >
    > >    You explain very well how operator= works.  I add operator[] to both
    > > class A and class B.  I add reference on both classes to the return
    > > type.
    > >    After operator[] receives index, it copies index into data member and
    > > then returns class A reference.
    > >    You may find out the trick.  Please look at my code.  Let me know
    > > what you think?

    >
    > <snip code>
    >
    > I see the trick you implemented, it can be a good idea but you did not
    > implement it very well.
    >
    > You left room for a lot of nonsensical instructions which lead to either
    > no effect or misleading effect.
    >
    > If an operation is not supported, you have to stop it at compile time,
    > so that the client code understands it's doing something that is not
    > expected to work - if your interface "seems" to support an operation but
    > in fact does just a no-op, that's just bad design.


    Well, you want flexibility. What choice do you want? Design only
    one class. The design is flawed. Always design small class when
    debug is easier to be tested.
    Define more than 5 classes. Group them into namespace. There are
    many different format designs such as graphics, word processor, or
    file types.
    Write good documentation. Give it to the programmers. The
    programmers know how to use code when they read documentation.
    Do you want to write one class name to do conversion?

    class ObjectToObject {
    // …
    };

    Maybe, you don’t want to do that.

    class Object_A {
    // …
    };

    class Object_B {
    // …
    };

    Object_A a;
    Object_B b;

    a = b;

    a[ x ] = b[ x ];

    Readability looks much easier to understand when you see variable
    names.


    > If an operation can not be executed with some inappropriate input, you
    > have to intercept that wrong input and either throw an exception or do a
    > no-op returning some kind of error flag.
    >
    > As it is, your code allows these bad instructions:
    >
    > a.Set_1(42, 10);
    >
    > The above is bad because writes out of the array boundaries, leading to
    > a wide series of well known problems. Always check the input before
    > feeding it as an array index.


    You do not need to do input check on subscript out of range if
    library is in release mode. You can always use assert to do input
    check in debug mode.

    >
    > a[1][2] = b[1][2];
    >
    > The above is bad because only the second series gets copied, while
    > somebody could think that also the first series will - and it won't.
    >
    > The solution to the above problem could be to /not/ return a Class_X&
    > from the subscript operator, but instead some different proxy class that
    > ensure the appropriate number and sequence of subscript operators - I
    > wouldn't consider implementing such proxies as a really easy task, but
    > maybe that's just due to my limited experience about this subject.


    Put three separate arrays into one array is bad idea because each
    array have different data types. To transfer data between two classes
    is the best when you want to index element in three arrays on the same
    time.

    > There is another issue with your class(es): duplicate code - a problem
    > into which I happen to fall quite often when starting a new program.
    >
    > If those classes share almost all the interface (and the underlying
    > data) all of this should be done with some common base class.


    > (if it were my code, I would also get rid of the duplicate code that
    > deals with those three arrays by putting them together in a further
    > array, accessing them by index instead that by different names as you
    > do... but if it were my code I would also use vectors instead of arrays)


    Sometimes, several classes have duplicate codes. Inheritance may be
    bad idea because internal data structures are different. Sometimes,
    you choose to use vector if you want to manipulate group of arrays.
    Different internal data structures cannot be shared between classes
    if they have different algorithms. First class uses algorithm to
    process data before data is transferred to second class and then
    second class receives data from first class to process different
    algorithm.
    You may not want to mix all algorithms into one class.


    > The mixed uppercase/lowercase/underscore naming style is quite weird to
    > my eyes, but this last issue is just matter of style/tastes.
    >
    > Well, you asked for my opinion, sorry if it seems too bad as a
    > dissection, I learnt to take the good things from such kind of replies,
    > when somebody else dissected my code - I hope you'll be able to do the
    > same with my reply.
    Immortal Nephi, Jul 21, 2010
    #5
  6. Immortal Nephi <>, on 20/07/2010 17:50:49, wrote:

    > On Jul 20, 6:24 pm, "Francesco S. Carta"<> wrote:
    >> Immortal Nephi<>, on 20/07/2010 14:45:22, wrote:
    >>
    >>> On Jul 18, 9:13 pm, "Francesco S. Carta"<> wrote:
    >>>> Immortal Nephi<>, on 18/07/2010 18:20:18, wrote:

    >>
    >> <large snip>
    >>
    >>>>> I can’t think how to add operator[] to both class a and class b.
    >>>>> Maybe, proxy class is needed. I tried to write operator[] function
    >>>>> but it did not work.

    >>
    >>>> You didn't post your operator[], which was the problem? I don't think
    >>>> there should be any problem in implementing it for both of your classes.

    >>
    >>> You explain very well how operator= works. I add operator[] to both
    >>> class A and class B. I add reference on both classes to the return
    >>> type.
    >>> After operator[] receives index, it copies index into data member and
    >>> then returns class A reference.
    >>> You may find out the trick. Please look at my code. Let me know
    >>> what you think?

    >>
    >> <snip code>
    >>
    >> I see the trick you implemented, it can be a good idea but you did not
    >> implement it very well.
    >>
    >> You left room for a lot of nonsensical instructions which lead to either
    >> no effect or misleading effect.
    >>
    >> If an operation is not supported, you have to stop it at compile time,
    >> so that the client code understands it's doing something that is not
    >> expected to work - if your interface "seems" to support an operation but
    >> in fact does just a no-op, that's just bad design.

    >
    > Well, you want flexibility. What choice do you want? Design only
    > one class. The design is flawed. Always design small class when
    > debug is easier to be tested.
    > Define more than 5 classes. Group them into namespace. There are
    > many different format designs such as graphics, word processor, or
    > file types.
    > Write good documentation. Give it to the programmers. The
    > programmers know how to use code when they read documentation.
    > Do you want to write one class name to do conversion?
    >
    > class ObjectToObject {
    > // …
    > };
    >
    > Maybe, you don’t want to do that.
    >
    > class Object_A {
    > // …
    > };
    >
    > class Object_B {
    > // …
    > };
    >
    > Object_A a;
    > Object_B b;
    >
    > a = b;
    >
    > a[ x ] = b[ x ];
    >
    > Readability looks much easier to understand when you see variable
    > names.
    >



    And messages look much easier to understand when phrases and paragraphs
    follow some kind of logical reasoning, possibly following up to the
    context they're replying to.

    In other words, I am not able to see what all the above has to deal with
    my assertion that your interfaces should allow only meaningful
    operations - unless we speak about over-abundant interfaces (or whatever
    they're called in English) meant to be used as base classes for
    different other classes, but this is another pair of legs, and a pair of
    legs that I don't really like.


    >
    >> If an operation can not be executed with some inappropriate input, you
    >> have to intercept that wrong input and either throw an exception or do a
    >> no-op returning some kind of error flag.
    >>
    >> As it is, your code allows these bad instructions:
    >>
    >> a.Set_1(42, 10);
    >>
    >> The above is bad because writes out of the array boundaries, leading to
    >> a wide series of well known problems. Always check the input before
    >> feeding it as an array index.

    >
    > You do not need to do input check on subscript out of range if
    > library is in release mode. You can always use assert to do input
    > check in debug mode.



    Writing out of the array boundaries is something you have to avoid by
    enforcing checks in your code whenever the index comes from client code
    - that is, whenever you aren't 100% sure that the index is within the
    valid range.

    The build mode, either "release" or "debug", has nothing to do with this
    issue.

    For the sake of context, this was your private array definition:

    unsigned char m_Data_1[ 4 ];

    and this was your public setter:

    Class_A &Class_A::Set_1( int index, unsigned char value ) {
    m_Data_1[ index ] = value;
    return *this;
    }

    The above is bad code, period.

    But if you are happy with a program/library that crashes, terminates or
    misbehaves after that an user mistypes a value, well that's your
    prerogative.


    >>
    >> a[1][2] = b[1][2];
    >>
    >> The above is bad because only the second series gets copied, while
    >> somebody could think that also the first series will - and it won't.
    >>
    >> The solution to the above problem could be to /not/ return a Class_X&
    >> from the subscript operator, but instead some different proxy class that
    >> ensure the appropriate number and sequence of subscript operators - I
    >> wouldn't consider implementing such proxies as a really easy task, but
    >> maybe that's just due to my limited experience about this subject.

    >
    > Put three separate arrays into one array is bad idea because each
    > array have different data types. To transfer data between two classes
    > is the best when you want to index element in three arrays on the same
    > time.



    Apart that you mixed up your reply sections (the part you're replying to
    appears further down) what you said doesn't match with the code you
    wrote. Both of your classes had the same exact data members, and all the
    arrays had the same exact type.

    Context, once more. These are the relevant parts of your code:

    class Class_A {

    // ...

    private:
    unsigned char m_Data_1[ 4 ];
    unsigned char m_Data_2[ 4 ];
    unsigned char m_Data_3[ 4 ];
    int m_Index;
    bool m_All;
    };

    class Class_B {

    // ...

    private:
    unsigned char m_Data_1[ 4 ];
    unsigned char m_Data_2[ 4 ];
    unsigned char m_Data_3[ 4 ];
    int m_Index;
    bool m_All;
    };


    >
    >> There is another issue with your class(es): duplicate code - a problem
    >> into which I happen to fall quite often when starting a new program.
    >>
    >> If those classes share almost all the interface (and the underlying
    >> data) all of this should be done with some common base class.

    >
    >> (if it were my code, I would also get rid of the duplicate code that
    >> deals with those three arrays by putting them together in a further
    >> array, accessing them by index instead that by different names as you
    >> do... but if it were my code I would also use vectors instead of arrays)

    >
    > Sometimes, several classes have duplicate codes. Inheritance may be
    > bad idea because internal data structures are different. Sometimes,
    > you choose to use vector if you want to manipulate group of arrays.
    > Different internal data structures cannot be shared between classes
    > if they have different algorithms. First class uses algorithm to
    > process data before data is transferred to second class and then
    > second class receives data from first class to process different
    > algorithm.
    > You may not want to mix all algorithms into one class.



    Once more, your internal data structures were identical to each other,
    and you posted no algorithms apart from the copying ones.

    The last classes you have posted can serenely be rewritten to share a
    common interface and a common data structure, still having different
    name and different type, and still they could use different algorithms
    to manipulate their data.

    But I see you're speaking in general or about some different code:
    either post that code or simply drop the current one, depending on what
    you want to discuss.

    --
    FSC - http://userscripts.org/scripts/show/59948
    http://fscode.altervista.org - http://sardinias.com
    Francesco S. Carta, Jul 21, 2010
    #6
  7. Immortal Nephi <> writes:
    <snip />

    > I can¡¯t think how to add operator[] to both class a and class b.
    > Maybe, proxy class is needed. I tried to write operator[] function
    > but it did not work.
    >
    > You can examine complete code below if you wish.


    Having seen your attempt at writing your op[] in a subsequent post I can
    agree with a lot of the comments made by Francesco in relation to it.
    Since you have mentioned the possibility of using a proxy class (and
    since we have looked at these together before also) I have quickly
    thrown together an example (very likely not ideal) to illustrate how
    this might be done. Since your classes store what are effectively three
    rows of four elements, I have implemented the proxy to manage `columns'
    of three elements via a tr1::tuple. This may not be viable nor what you
    want but, as I have said, this is just an example.

    I present my example by making additions to the code you have already
    given (below).

    /***** CODE *****/

    #include <tr1/tuple>

    template<typename T>
    class Column_Proxy {
    template<typename U>
    friend class Column_Proxy;
    public:
    typedef std::tr1::tuple< T&, T&, T& > column_t;
    Column_Proxy( T& first, T& second, T& third )
    : column( first, second, third )
    { }
    template<typename U>
    Column_Proxy& operator=( const Column_Proxy<U>& cp ) {
    std::tr1::get< 0 >( column ) = std::tr1::get< 0 >( cp.column );
    std::tr1::get< 1 >( column ) = std::tr1::get< 1 >( cp.column );
    std::tr1::get< 2 >( column ) = std::tr1::get< 2 >( cp.column );
    return *this;
    }

    operator column_t( ) { return column; }
    operator column_t( ) const { return column; }
    private:
    column_t column;
    };

    // class Class_A; forward declaration of Class_A not needed
    > class Class_B;
    >
    > class Class_A {
    > public:
    > Class_A();
    > Class_A( const Class_A &class_a );
    > ~Class_A();
    >
    > Class_A &Set_1( int index, unsigned char value );
    > Class_A &Set_2( int index, unsigned char value );
    > Class_A &Set_3( int index, unsigned char value );
    >
    > unsigned char Get_1( int index );
    > unsigned char Get_2( int index );
    > unsigned char Get_3( int index );
    >
    > operator Class_B ();


    Column_Proxy< unsigned char > operator[ ] ( int i );
    Column_Proxy< const unsigned char > operator[ ] ( int i ) const;

    > private:
    > unsigned char m_Data_1[ 4 ];
    > unsigned char m_Data_2[ 4 ];
    > unsigned char m_Data_3[ 4 ];
    > };
    >
    > class Class_B {
    > public:
    > Class_B();
    > Class_B( const Class_B &class_b );
    > ~Class_B();
    >
    > Class_B &Set_1( int index, unsigned char value );
    > Class_B &Set_2( int index, unsigned char value );
    > Class_B &Set_3( int index, unsigned char value );
    >
    > unsigned char Get_1( int index );
    > unsigned char Get_2( int index );
    > unsigned char Get_3( int index );
    >
    > operator Class_A ();


    Column_Proxy< unsigned char > operator[ ] ( int i );
    Column_Proxy< const unsigned char > operator[ ] ( int i ) const;

    > private:
    > unsigned char m_Data_1[ 4 ];
    > unsigned char m_Data_2[ 4 ];
    > unsigned char m_Data_3[ 4 ];
    > };
    >
    > Class_A::Class_A() {
    > for( int x = 0; x < 4; x++ )
    > m_Data_1[ x ] = m_Data_2[ x ] = m_Data_3[ x ] = 0;
    > }
    >
    > Class_A::Class_A( const Class_A &class_a ) {
    > for( int x = 0; x < 4; x++ ) {
    > m_Data_1[ x ] = class_a.m_Data_1[ x ];
    > m_Data_2[ x ] = class_a.m_Data_2[ x ];
    > m_Data_3[ x ] = class_a.m_Data_3[ x ];
    > }
    > }
    >
    > Class_A::~Class_A() {
    > }
    >
    > Class_A &Class_A::Set_1( int index, unsigned char value ) {
    > m_Data_1[ index ] = value;
    > return *this;
    > }
    >
    > Class_A &Class_A::Set_2( int index, unsigned char value ) {
    > m_Data_2[ index ] = value;
    > return *this;
    > }
    >
    > Class_A &Class_A::Set_3( int index, unsigned char value ) {
    > m_Data_3[ index ] = value;
    > return *this;
    > }
    >
    > unsigned char Class_A::Get_1( int index ) {
    > return m_Data_1[ index ];
    > }
    >
    > unsigned char Class_A::Get_2( int index ) {
    > return m_Data_2[ index ];
    > }
    >
    > unsigned char Class_A::Get_3( int index ) {
    > return m_Data_3[ index ];
    > }
    >
    > Class_A::eek:perator Class_B () {
    > Class_B b;
    >
    > for( int x = 0; x < 4; x++ )
    > b.Set_1( x, m_Data_1[ x ] )
    > .Set_2( x, m_Data_2[ x ] )
    > .Set_3( x, m_Data_3[ x ] );
    >
    > return b;
    > }


    Column_Proxy< unsigned char >
    Class_A::eek:perator[ ] ( int i ) {
    return Column_Proxy< unsigned char >( m_Data_1[ i ],
    m_Data_2[ i ],
    m_Data_3[ i ]);
    }

    Column_Proxy< const unsigned char >
    Class_A::eek:perator[ ] ( int i ) const {
    return Column_Proxy< const unsigned char >( m_Data_1[ i ],
    m_Data_2[ i ],
    m_Data_3[ i ]);
    }


    >
    > Class_B::Class_B() {
    > for( int x = 0; x < 4; x++ )
    > m_Data_1[ x ] = m_Data_2[ x ] = m_Data_3[ x ] = 0;
    > }
    >
    > Class_B::Class_B( const Class_B &class_b ) {
    > for( int x = 0; x < 4; x++ ) {
    > m_Data_1[ x ] = class_b.m_Data_1[ x ];
    > m_Data_2[ x ] = class_b.m_Data_2[ x ];
    > m_Data_3[ x ] = class_b.m_Data_3[ x ];
    > }
    > }
    >
    > Class_B::~Class_B() {
    > }
    >
    > Class_B &Class_B::Set_1( int index, unsigned char value ) {
    > m_Data_1[ index ] = value;
    > return *this;
    > }
    >
    > Class_B &Class_B::Set_2( int index, unsigned char value ) {
    > m_Data_2[ index ] = value;
    > return *this;
    > }
    >
    > Class_B &Class_B::Set_3( int index, unsigned char value ) {
    > m_Data_3[ index ] = value;
    > return *this;
    > }
    >
    > unsigned char Class_B::Get_1( int index ) {
    > return m_Data_1[ index ];
    > }
    >
    > unsigned char Class_B::Get_2( int index ) {
    > return m_Data_2[ index ];
    > }
    >
    > unsigned char Class_B::Get_3( int index ) {
    > return m_Data_3[ index ];
    > }
    >
    > Class_B::eek:perator Class_A () {
    > Class_A a;
    >
    > for( int x = 0; x < 4; x++ )
    > a.Set_1( x, m_Data_1[ x ] )
    > .Set_2( x, m_Data_2[ x ] )
    > .Set_3( x, m_Data_3[ x ] );
    >
    > return a;
    > }


    Column_Proxy< unsigned char >
    Class_B::eek:perator[ ] ( int i ) {
    return Column_Proxy< unsigned char >( m_Data_1[ i ],
    m_Data_2[ i ],
    m_Data_3[ i ] );
    }

    Column_Proxy< const unsigned char >
    Class_B::eek:perator[ ] ( int i ) const {
    return Column_Proxy< const unsigned char >( m_Data_1[ i ],
    m_Data_2[ i ],
    m_Data_3[ i ] );
    }

    > int main() {
    > Class_A a;
    > Class_B b;
    >
    > a.Set_1( 0, 0x41 ).Set_2( 0, 0x42 ).Set_3( 0, 0x43 );
    > a.Set_1( 1, 0x44 ).Set_2( 1, 0x45 ).Set_3( 1, 0x46 );
    > a.Set_1( 2, 0x47 ).Set_2( 2, 0x48 ).Set_3( 2, 0x49 );
    > a.Set_1( 3, 0x4a ).Set_2( 3, 0x4b ).Set_3( 3, 0x4c );
    >
    > b.Set_1( 0, 0x71 ).Set_2( 0, 0x72 ).Set_3( 0, 0x73 );
    > b.Set_1( 1, 0x74 ).Set_2( 1, 0x75 ).Set_3( 1, 0x76 );
    > b.Set_1( 2, 0x77 ).Set_2( 2, 0x78 ).Set_3( 2, 0x79 );
    > b.Set_1( 3, 0x7a ).Set_2( 3, 0x7b ).Set_3( 3, 0x7c );
    >
    > // First method
    > for( int x = 0; x < 4; x++ )
    > a.Set_1( x, b.Get_1( x ) )
    > .Set_2( x, b.Get_2( x ) )
    > .Set_3( x, b.Get_3( x ) );
    >
    > // Second method
    > a = b;
    >
    > // Third method
    > for( int x = 0; x < 4; x++ )
    > a[ x ] = b[ x ];
    >
    > return 0;
    > }


    /***** END CODE *****/

    Just an idea.

    Regards

    Paul Bibbings
    Paul Bibbings, Jul 21, 2010
    #7
    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. MSG

    to cast or not to cast malloc ?

    MSG, Feb 6, 2004, in forum: C Programming
    Replies:
    38
    Views:
    1,063
    Dan Pop
    Feb 10, 2004
  2. EvilRix
    Replies:
    8
    Views:
    624
    Martin Dickopp
    Feb 14, 2004
  3. John Goche
    Replies:
    2
    Views:
    338
    Frederick Gotham
    Sep 4, 2006
  4. Immortal Nephi

    Operator Cast () Reference?

    Immortal Nephi, Jun 10, 2010, in forum: C++
    Replies:
    23
    Views:
    971
    Paul Bibbings
    Jun 12, 2010
  5. Pavel
    Replies:
    7
    Views:
    515
    Pavel
    Sep 19, 2010
Loading...

Share This Page