home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #20 / NN_1992_20.iso / spool / comp / lang / cplus / 13652 < prev    next >
Encoding:
Text File  |  1992-09-14  |  2.7 KB  |  88 lines

  1. Newsgroups: comp.lang.c++
  2. Path: sparky!uunet!uunet.ca!frumious!pat
  3. From: pat@frumious.uucp (Patrick Smith)
  4. Subject: Re: Default assignment semantics
  5. Message-ID: <1992Sep15.025550.519@frumious.uucp>
  6. Date: Tue, 15 Sep 1992 02:55:50 GMT
  7. Reply-To: uunet.ca!frumious!pat
  8. References: <BuJs8K.Hzr@vcd.hp.com>
  9. Organization: None
  10. Lines: 76
  11.  
  12. John Matthews <jm@vcd.hp.com> writes:
  13. |It seems to me that a more robust default assignment operator would
  14. |have the following semantics:
  15. |
  16. |  1) Invoke the destructor of the object being assigned to.
  17. |  2) Invoke the copy constructor on the object being assigned
  18. |      to, taking as an argument the object being assigned from.
  19.  
  20. |Thus what I want to do in most of my assignment operators is
  21. |explicitly invoke the corresponding destructor and copy constructor.
  22.  
  23. |        X& X::operator=( const X& from )
  24. |        {
  25. |                  this->~X();               // invoke destructor.
  26. |                  new(this) X(from);        // invoke copy constructor.
  27. |                  return( *this );
  28. |        }
  29.  
  30.  
  31. First, there's a small bug in your assignment operator -
  32. consider what happens if this == &from.
  33.  
  34.  
  35. More importantly, I would suggest that, while many specific assignment
  36. operators can be safely implemented this way, doing it as a general
  37. rule would not be a good idea.  Consider, for example,
  38.  
  39.    class X {
  40.    public:
  41.       X& operator=(const X& from);   // implemented as above
  42.       virtual foo();
  43.    };
  44.  
  45. So far, so good.  Now someone derives a new class:
  46.  
  47.    class Y : public X {
  48.    public:
  49.       Y& operator=(const Y& from);
  50.       virtual foo();
  51.    };
  52.  
  53.    Y& Y::operator=(const Y& from) {
  54.       if (this != &from) {
  55.          X::operator=(from);      // Copy the X part
  56.          // Do some customized copying of the Y stuff
  57.       }
  58.    }
  59.  
  60.    void g(Y& y1, Y& y2) {
  61.       y1 = y2;
  62.       y1.foo();
  63.    }
  64.  
  65. What does the call to y1.foo() inside g() do?  Quite probably, it
  66. will call X::foo()!
  67.  
  68. The problem is that Y's assignment operator calls X's assignment
  69. operator, which calls the destructor for y1 (and calls the wrong
  70. destructor, unless X's destructor is virtual), and then creates
  71. a new X object on top of y1.  Not quite what the programmer intended!
  72.  
  73. So if you use this style of assignment operator in a class X,
  74. then all(*) classes derived from X should avoid calling X's assignment
  75. operator inside their own assignment operators (or anywhere else,
  76. for that matter).  In general, I'd think it would be worth a bit
  77. of extra coding to avoid this restriction.
  78.  
  79. footnote:
  80. (*) Maybe you don't need to impose this restriction if X has no
  81. virtual functions and no virtual base classes.  But that would
  82. depend on the implementation.
  83.  
  84. -- 
  85. Patrick Smith
  86. uunet.ca!frumious!pat
  87. pat%frumious.uucp@uunet.ca
  88.