How to use reference to do cast operator?

I

Immortal Nephi

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;
}
 
F

Francesco S. Carta

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.
 
I

Immortal Nephi

   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;
}
 
F

Francesco S. Carta

Immortal Nephi<[email protected]>, on 18/07/2010 18:20:18, wrote:
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++
 
I

Immortal Nephi

Immortal Nephi<[email protected]>, on 18/07/2010 18:20:18, wrote:
    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.
 
F

Francesco S. Carta

Immortal Nephi<[email protected]>, on 18/07/2010 18:20:18, wrote:

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.

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;
};

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.
 
P

Paul Bibbings

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 said:
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 said:
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,965
Messages
2,570,148
Members
46,710
Latest member
FredricRen

Latest Threads

Top