473,320 Members | 2,145 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,320 software developers and data experts.

Virtual Assignment Operator in Protocol class

N4M
Dear,
Suppose I have a Protocol class, in which I need also an assignment
operator =().
class B
{
.....
virtual B& operator=(const B& rb) =0;
};
Now in some derived class D: public B, how would I proceed with
operator=? Do I need to supply 2 operators:
-01 to override D& operator=(const B& d)
-and 01 to overload D& operator=(const D& d)?
How about the (im)purality of = ?
Thanks for your guidance.
Jul 22 '05 #1
17 3703
N4M wrote:

Dear,
Suppose I have a Protocol class, in which I need also an assignment
operator =().
class B
{
....
virtual B& operator=(const B& rb) =0;
};
Now in some derived class D: public B, how would I proceed with
operator=? Do I need to supply 2 operators:
-01 to override D& operator=(const B& d)
-and 01 to overload D& operator=(const D& d)?
How about the (im)purality of = ?
Thanks for your guidance.


In a nutshell: having op= as virtual is seldome a good idea. Mostly
because it doesn't work how most people would expect it to work.
Hint: polymorphism works only by examining the object the function
is called for, but doesn't take the runtime type of the arguments
into account.

Assume class D, derived from class B

B* p1 = new D;
B* p2 = new D;

*p1 = *p2;

In the above, what do you think the compiler is looking for
when searching a function to fullfil the request of the
assignment? :

1) B::operator=( const B& Arg );
or 2) B::operator=( const D& Arg );
or 3) D::operator=( const B& Arg );
or 4) D::operator=( const D& Arg );

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 22 '05 #2
Karl Heinz Buchegger wrote:

N4M wrote:

Dear,
Suppose I have a Protocol class, in which I need also an assignment
operator =().
class B
{
....
virtual B& operator=(const B& rb) =0;
};
Now in some derived class D: public B, how would I proceed with
operator=? Do I need to supply 2 operators:
-01 to override D& operator=(const B& d)
-and 01 to overload D& operator=(const D& d)?
How about the (im)purality of = ?
Thanks for your guidance.
In a nutshell: having op= as virtual is seldome a good idea. Mostly
because it doesn't work how most people would expect it to work.
Hint: polymorphism works only by examining the object the function
is called for, but doesn't take the runtime type of the arguments
into account.


After rethinking (and smoking a cigarette) I don't think this to be
relevant any more. Read on ...

Assume class D, derived from class B

B* p1 = new D;
B* p2 = new D;

*p1 = *p2;

In the above, what do you think the compiler is looking for
when searching a function to fullfil the request of the
assignment? :

1) B::operator=( const B& Arg );
or 2) B::operator=( const D& Arg );
or 3) D::operator=( const B& Arg );
or 4) D::operator=( const D& Arg );


Next thought experiment (you might want to try this with your compiler
anyway): make the operator virtual.
Which op= is called then?

Hint: Think about what a dereference operation does when the runtime
type of an object differs from the static type of the pointer pointing
to it.

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 22 '05 #3

"Karl Heinz Buchegger" <kb******@gascad.at> wrote in message
news:41***************@gascad.at...

In a nutshell: having op= as virtual is seldome a good idea. Mostly
because it doesn't work how most people would expect it to work.
Hint: polymorphism works only by examining the object the function
is called for, but doesn't take the runtime type of the arguments
into account.
Then programmer must do that, no?
Assume class D, derived from class B

B* p1 = new D;
B* p2 = new D;

*p1 = *p2;

In the above, what do you think the compiler is looking for
when searching a function to fullfil the request of the
assignment? :

1) B::operator=( const B& Arg );
or 2) B::operator=( const D& Arg );
or 3) D::operator=( const B& Arg );
or 4) D::operator=( const D& Arg );
The symptoms you demonstrate do not exist (nor become better
or worse) because of virtual assignment, but because of how
C++ object model works. In real world, virtual assignment is not
the disease, but the cure!

Consider this example:

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

struct B
{
// this is a pretty canonical class, except for virtual assignment

virtual B &operator=( const B & );

// virtual destructor needed for derived deletion thru base *

virtual ~B();

// add more members (data or functions) to suit the taste...
};
struct D : public B
{
int value;

D( int i=0 ) : value(i)
{
}

D( const D &r ) : B(r) , value(r.value)
{
}

virtual D &operator=( const D &r )
{
B::operator=( r );
value = r.value;
return *this;
}

virtual B &operator=( const B &r )
{
const D *p = dynamic_cast<const D *>(&r);

if( p )
{
*this = *p;
}
else
{
// D is being sliced, but now you can detect and compensate!!!
B::operator=( r );
value = 0;
}

return *this;
}
};

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

Now your example...
B* p1 = new D;
B* p2 = new D;

*p1 = *p2;
.... will assign as-if using the most intuitive choice...
1) B::operator=( const B& Arg );
or 2) B::operator=( const D& Arg );
or 3) D::operator=( const B& Arg );
or 4) D::operator=( const D& Arg );


.... which, of course, is 4 .

- Risto -
Jul 22 '05 #4
Risto Lankinen wrote:


... which, of course, is 4 .

- Risto -


Thanks for bringing me back on track.
I don't know why, but somehow I always get lost when
thinking about a virtual op= :-)
--
Karl Heinz Buchegger
kb******@gascad.at
Jul 22 '05 #5
N4M
"Risto Lankinen" <rl******@hotmail.com> wrote in message news:<fE*******************@news1.nokia.com>...
"Karl Heinz Buchegger" <kb******@gascad.at> wrote in message
news:41***************@gascad.at...

Thanks for your example, but what if it is insisted to have all
operators and functions in the ABC be PURE ? Then I cannot initiate B
b...
That's driving my crazy.
N4M
Jul 22 '05 #6
N4M
"Risto Lankinen" <rl******@hotmail.com> wrote in message news:<fE*******************@news1.nokia.com>...
"Karl Heinz Buchegger" <kb******@gascad.at> wrote in message
news:41***************@gascad.at...

Thanks for your example, but what if it is insisted to have all
operators and functions in the ABC be PURE ? Then I cannot initiate B
b...
That's driving my crazy.
N4M
Jul 22 '05 #7
N4M wrote:

"Risto Lankinen" <rl******@hotmail.com> wrote in message news:<fE*******************@news1.nokia.com>...
"Karl Heinz Buchegger" <kb******@gascad.at> wrote in message
news:41***************@gascad.at...

Thanks for your example, but what if it is insisted to have all
operators and functions in the ABC be PURE ? Then I cannot initiate B
b...
That's driving my crazy.


I don't understand.

Making those functions PURE (by adding = 0), doesn't mean that
you cannot provide an implementation for them :-)

--
Karl Heinz Buchegger
kb******@gascad.at
Jul 22 '05 #8
dn******@yahoo.com (N4M) wrote:
Dear,
Suppose I have a Protocol class, in which I need also an assignment
operator =().
class B
{
....
virtual B& operator=(const B& rb) =0;
};
Now in some derived class D: public B, how would I proceed with
operator=?
Let's consider this tree:

class B {
public:
virtual ~B() { }
virtual B& operator=( const B& ) = 0;
};

class D {
int foo;
public:
virtual B& operator=( const B& );
};

class C {
double bar;
public:
virtual B& operator=( const B& );
};

Can either D::op= or C::op= be implemented reasonably? I don't think so.
What is supposed to happen with this code?

void fun( B& b1, B& b2 ) {
b1 = b2;
}

If b1 is an D and b2 is a C?

Do I need to supply 2 operators:
-01 to override D& operator=(const B& d)
You need to provide this one.
-and 01 to overload D& operator=(const D& d)?


You would only need this one if something in the class requires it,
otherwise it will be created properly for you.

The moral of the story here is "don't provide a pure virtual op=".
Jul 22 '05 #9

"Daniel T." <po********@eathlink.net> wrote in message news:postmaster->
Let's consider this tree:

class B {
public:
virtual ~B() { }
virtual B& operator=( const B& ) = 0;
};

class D {
int foo;
public:
virtual B& operator=( const B& );
};

class C {
double bar;
public:
virtual B& operator=( const B& );
};

Can either D::op= or C::op= be implemented reasonably?
Absolutely. Here's one way:

B &D::operator=( const B &r )
{
const D *p = dynamic_cast<const D *>(&r);
if( p )
foo = p->foo;
else
throw "Silly assignment!";
return *this;
}

B &C::operator=( const B &r )
{
const C *p = dynamic_cast<const C *>(&r);
if( p )
bar = p->bar;
else
throw "Silly assignment!";
return *this;
}
What is supposed to happen with this code?

void fun( B& b1, B& b2 ) {
b1 = b2;
}

If b1 is an D and b2 is a C?
With virtual assignment, it would throw a "Silly assignment!"
exception (or alternatively, whatever else the author of the
class D deems appropriate for the case when, so to speak,
an Orange is assigned to a Banana thru a reference to Fruit).

WithOUT virtual assignment it would create a banana the size
of a grape, with an orange skin that doesn't need to be peeled
before eating.
The moral of the story here is "don't provide a pure virtual op=".


This belief, I think, needs reconsideration.

- Risto -
Jul 22 '05 #10
In article <Iu*******************@news1.nokia.com>,
"Risto Lankinen" <rl******@hotmail.com> wrote:
"Daniel T." <po********@eathlink.net> wrote in message news:postmaster->
Let's consider this tree:

class B {
public:
virtual ~B() { }
virtual B& operator=( const B& ) = 0;
};

class D {
int foo;
public:
virtual B& operator=( const B& );
};

class C {
double bar;
public:
virtual B& operator=( const B& );
};

Can either D::op= or C::op= be implemented reasonably?


Absolutely. Here's one way:

B &D::operator=( const B &r )
{
const D *p = dynamic_cast<const D *>(&r);
if( p )
foo = p->foo;
else
throw "Silly assignment!";
return *this;
}

B &C::operator=( const B &r )
{
const C *p = dynamic_cast<const C *>(&r);
if( p )
bar = p->bar;
else
throw "Silly assignment!";
return *this;
}


I wouldn't call that a reasonable implementation, but maybe that's just
me. If the "Silly assignment" is thrown, the calling code can't do
anything resonable with it and in order for the calling code to avoid
the throw, it must use RTTI to ensure both arguments are the same
derived type, then call op= which again uses dynamic_cast. That sounds
quite expensive...
Jul 22 '05 #11
N4M
> This belief, I think, needs reconsideration.

- Risto -


How do you think about this solution:

class D; //forward
class B
{
public:
virtual ~B() = 0;
virtual B& operator=(const B&b) = 0;
virtual void Assign(D&) const = 0;
};
B::~B() {}
class D: public B
{
public:
explicit D(int i):m_n(i) {}
~D() {}
B& operator=(const B& rb) {
rb.Assign(*this);
return dynamic_cast<B&>(*this);//static_cast is fine, too.
}
virtual void Assign(D& rd) const { rd.m_n = m_n;}
int GetValue() {return m_n;}
private:
int m_n;

};
// Test
void Test(B& b1, B& b2)
{
b1 = b2;
}
//
int main()
{
D d1(1), d2(2);
cout<<"Before d1, d2:"<< d1.GetValue() <<","<<d2.GetValue()<<endl;
Test(d1,d2);
cout<<"After d1, d2:"<< d1.GetValue() <<","<<d2.GetValue()<<endl;
}
I have tested this, it's fine, and it serves the purpose that all
Derived classes need to define the assignment operator indirectly thru
Assign().
Nguyen Mai.
Jul 22 '05 #12

"Daniel T." <po********@eathlink.net> wrote in message
news:po******************************@news04.east. earthlink.net...

If the "Silly assignment" is thrown, the calling code can't do
anything resonable with it...
First, I'll repaste a section deleted from my original article:
With virtual assignment, it would throw a "Silly assignment!"
exception (or alternatively, whatever else the author of the
class D deems appropriate ...


If you deem the default behaviour (by which I mean simulating
the non-virtual assignment) more appropriate, you could do the
following:

B &D::operator=( const B &r )
{
const D *p = dynamic_cast<const D *>(&r);'
if( p )
// Calls D::operator=( const D & ); Explicit call deliberately
// not used to enable late binding for D::op=(D) in case it has
// been declared virtual in this class and similarly overloaded
// in the next level derived class:
return *this = *p;
else
return B::operator=( p );
}
... and in order for the calling code to avoid the throw it must
use RTTI to ensure both arguments are the same derived type,
then call op= which again uses dynamic_cast. That sounds
quite expensive...


If RTTI is too costly, simply leave it away! But then you can't
completely emulate Derived-to-Derived assignment thru Base
pointers or references. This will still be useful for detecting the
slicing (e.g. to retain the class-level invariants of D consistent
even if someone assigns only a Base part to it):

B &D::operator=( const B &r )
{
B &result = B::operator=( r );
// This 'D' was just sliced by a 'B' called 'r'! Counteract!
return result;
}

Without virtual assignment in base class, this would be impossible.

Cheers!

- Risto -
Jul 22 '05 #13
In article <6e**************************@posting.google.com >,
dn******@yahoo.com (N4M) wrote:
This belief, I think, needs reconsideration.

- Risto -


How do you think about this solution:

class D; //forward
class B
{
public:
virtual ~B() = 0;
virtual B& operator=(const B&b) = 0;
virtual void Assign(D&) const = 0;
};
B::~B() {}
class D: public B
{
public:
explicit D(int i):m_n(i) {}
~D() {}
B& operator=(const B& rb) {
rb.Assign(*this);
return dynamic_cast<B&>(*this);//static_cast is fine, too.
}
virtual void Assign(D& rd) const { rd.m_n = m_n;}
int GetValue() {return m_n;}
private:
int m_n;

};
// Test
void Test(B& b1, B& b2)
{
b1 = b2;
}
//
int main()
{
D d1(1), d2(2);
cout<<"Before d1, d2:"<< d1.GetValue() <<","<<d2.GetValue()<<endl;
Test(d1,d2);
cout<<"After d1, d2:"<< d1.GetValue() <<","<<d2.GetValue()<<endl;
}
I have tested this, it's fine, and it serves the purpose that all
Derived classes need to define the assignment operator indirectly thru
Assign().


You end up needing to add an assign to the base class and implement a
new assign method in every other derived class whenever you to create a
derived class. Very ugly...
Jul 22 '05 #14
dn******@yahoo.com (N4M) wrote:
Dear,
Suppose I have a Protocol class, in which I need also an assignment
operator =().
class B
{
....
virtual B& operator=(const B& rb) =0;
};
Now in some derived class D: public B, how would I proceed with
operator=? Do I need to supply 2 operators:
-01 to override D& operator=(const B& d)
-and 01 to overload D& operator=(const D& d)?
How about the (im)purality of = ?
Thanks for your guidance.


I've been thinking more about this whole situation... Given the above
class (ie op= is pure virtual and the class has no data members) what is
the post-condition of the op=? I mean, usually the post-condition of op=
is that *this == rb after the function, but would:

bool fun( B& r, B& l ) {
return r == l;
}

even compile? If yes, under what conditions should the above return
false? Under what condtions should it return true?
Jul 22 '05 #15
"Risto Lankinen" <rl******@hotmail.com> wrote:
Consider this example: [formatting changed some to save space. I also added a pure virtual
to the base to make it an ABC like the origional example.]
// ========================================

struct B
{ virtual void foo() = 0; virtual B &operator=( const B & );
virtual ~B();
};
struct D : public B
{
int value;
D( int i=0 ) : value(i) { }

D( const D &r ) : B(r) , value(r.value) { } void foo() { }
virtual D &operator=( const D &r ) {
B::operator=( r );
value = r.value;
return *this;
}

virtual B &operator=( const B &r ) {
const D *p = dynamic_cast<const D *>(&r);
if( p ) {
*this = *p;
}
else {
// D is being sliced, but now you can detect and compensate!!!
B::operator=( r );
value = 0;
}
return *this;
}
};


Using the example above, given this code:

struct E: public B {
float value;
B(): value( 0.01 ) { }
void foo() { }
B& operator=( const B& r ) {
const E* p = dynamic_cast< const E* >( &r );
if ( p )
*this = *p;
else {
B::operator=( r );
value = 0.01;
}
return *this;
}
};

void ick( B& l, B& r ) {
l = r;
}

int main() {
E e;
D d;

ick( d, e );
}

Are 'd' and 'e' supposed to be equal after the call to 'ick'? Could 'd'
and 'e' ever be equal? If not, then what is B's op= supposed to do?
Jul 22 '05 #16

"Daniel T." <po********@eathlink.net> wrote in message
news:po******************************@news03.east. earthlink.net...
"Risto Lankinen" <rl******@hotmail.com> wrote:
Consider this example: [formatting changed some to save space. I also added a pure virtual
to the base to make it an ABC like the origional example.]

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

struct B
{

virtual void foo() = 0;
virtual B &operator=( const B & );
virtual ~B();
};
struct D : public B
{
int value;
D( int i=0 ) : value(i) { }

D( const D &r ) : B(r) , value(r.value) { }

void foo() { }

virtual D &operator=( const D &r ) {
B::operator=( r );
value = r.value;
return *this;
}

virtual B &operator=( const B &r ) {
const D *p = dynamic_cast<const D *>(&r);
if( p ) {
*this = *p;
}
else {
// D is being sliced, but now you can detect and compensate!!!
B::operator=( r );
value = 0;
}
return *this;
}
};


Using the example above, given this code:

struct E: public B {
float value;
B(): value( 0.01 ) { }
void foo() { }
B& operator=( const B& r ) {
const E* p = dynamic_cast< const E* >( &r );
if ( p )
*this = *p;
else {
B::operator=( r );
value = 0.01;
}
return *this;
}
};

void ick( B& l, B& r ) {
l = r;
}

int main() {
E e;
D d;

ick( d, e );
}

Are 'd' and 'e' supposed to be equal after the call to 'ick'?


Well, yes, if you use the same definition of "equal" as
when B &B::op=(B) is non-virtual.
Could 'd' and 'e' ever be equal?


You tell me. Sure you knew that if you implemented
bool operator(B,B) you'd be able compare a C to a D
anyway (again, regardless of whether the assignments
occurred thru virtual or non-virtual operators).

Nevertheless, I don't see it as a big problem that op=()
and op==() lose a bit of consistency in the presence of
type conversions. Even C with no operator overloading
or virtual methods manifests a similar inconsistency:

int i = 12;
double d = 3.4;
i = d;
assert( i == d );
Cheers!

- Risto -
Jul 22 '05 #17
In article <pD*******************@news1.nokia.com>,
"Risto Lankinen" <rl******@hotmail.com> wrote:
"Daniel T." <po********@eathlink.net> wrote in message
news:po******************************@news03.east. earthlink.net...
"Risto Lankinen" <rl******@hotmail.com> wrote:

void ick( B& l, B& r ) {
l = r;
}

int main() {
E e;
D d;

ick( d, e );
}

Are 'd' and 'e' supposed to be equal after the call to 'ick'?
Well, yes, if you use the same definition of "equal" as
when B &B::op=(B) is non-virtual.


That's a little hard to do when B::op=(B) is pure-virtual...
Could 'd' and 'e' ever be equal?


You tell me. Sure you knew that if you implemented
bool operator==(B,B) you'd be able compare a C to a D
anyway (again, regardless of whether the assignments
occurred thru virtual or non-virtual operators).


Let's do that:

struct Base {
int x;
Base( int a ): x(a) { }
virtual ~Base() { }
};

bool operator==( const Base& l, const Base& r ) {
return l.x == r.x;
}

//================================================== ==================
struct Derived : Base {
int y;
Derived( int a, int b ): Base( a ), y( b ) { }
};

//================================================== ==================
void comp( Base& l, Base& r ) {
assert( l == r );
}

int main() {
Derived d1( 0, 1 ), d2( 0, 2 );
comp( d1, d2 );
cout << "OK";
}

(The above prints "OK")
Is this really something anyone would want?
Nevertheless, I don't see it as a big problem that op=()
and op==() lose a bit of consistency in the presence of
type conversions. Even C with no operator overloading
or virtual methods manifests a similar inconsistency:

int i = 12;
double d = 3.4;
i = d;
assert( i == d );


And that causes no end of trouble for those who think that double should
be substitutable for int. It isn't.

By using inheritance (rather than say a conversion function) we are
saying that any type Derived from Base is subsitutable for it.
Jul 22 '05 #18

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

Similar topics

18
by: nenad | last post by:
Wouldn't it be nice if we could do something like this: class Funky{ public: auto virtual void doStuff(){ // dostuff } };
3
by: Daniel Graifer | last post by:
Why doesn't c++ support virtual data? It supports class data of type pointer to function (that's what virtual functions really are). In terms of implementation, why can't I have other types of...
15
by: Heiner | last post by:
#include <stdio.h> class A { public: virtual A & operator= (const A &); virtual void test(const A &); }; class B : public A
7
by: Calum | last post by:
Hi, I have a base class called Number that has subclasses Integer and Float. I want to be able to provide a virtual operator- in the base class so that I can subtract one Integer from another...
2
by: gyarnell | last post by:
Just ran into a problem upgrading from GCC 2.96 to GCC 3.3.2. There is a bug somewhere, could be in 2.96, could be in 3.3.2, could be in the code I'm compiling. Here's a minimal example class A {...
11
by: Nindi73 | last post by:
A few days a ago I posted my code for a deep copy pointer which doesn't require the pointee object to have a virtual copy constructor. I need help with checking that it was exception safe and...
2
by: sven.bauer | last post by:
Hi, I have a question following up the following slightly older posting: http://groups.google.de/group/comp.lang.c++/browse_thread/thread/40e52371e89806ae/52a3a6551f84d38b class Base {...
17
by: Jess | last post by:
Hello, If I have a class that has virtual but non-pure declarations, like class A{ virtual void f(); }; Then is A still an abstract class? Do I have to have "virtual void f() = 0;"...
8
by: sun1991 | last post by:
Hi All, I tried the following code, but it did not work as I think: --- using namespace std; namespace { class Fraction { public:
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
0
by: Vimpel783 | last post by:
Hello! Guys, I found this code on the Internet, but I need to modify it a little. It works well, the problem is this: Data is sent from only one cell, in this case B5, but it is necessary that data...
0
by: jfyes | last post by:
As a hardware engineer, after seeing that CEIWEI recently released a new tool for Modbus RTU Over TCP/UDP filtering and monitoring, I actively went to its official website to take a look. It turned...
0
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

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

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