Topic: Proposal for fixing a problem with default assignment


Author: William Sears <sears@zork.tiac.net>
Date: 1995/10/21
Raw View
The current default definition of assignment for C++ produces
incorrect code in most cases where either a copy constructor or
a destructor present, thus requiring the programmer to override
the default. A suitable default exists that gets around the
problem.  It can be introduced in a manner that is upwardly
compatible for nearly all correct programs.

The change is believed to affect very few correctly working
programs, because it is widely known that introducing either a
copy constructor or a destructor "requires" an assignment
operator. Programs written to this rule would be not be affected.
Programs that did not follow this rule might very well be fixed
by the introduction of this change.

I believe the change would reduce programming errors, simplify
writing of many classes, and allow the topic of overloading
assignment to be deferred when teaching C++.


The proposed change:


       If there is:

              a.  No assignment operator
              b.  Either a user defined copy constructor
                    OR Defined destructor.

       Then the definition of Class operator=(const Class& other)

       is:

               if (this==&src)
                   return *this;
               Class::~Class();
               new (this) Class(src);
               return *this;

    This definition is only used for copy/assignment.

        Thus assignment becomes semantically:

        . Destroy the current contents of the location
           be assigned.

        . Copy the new contents into the location being assigned.


The advantages of this change:

    1.  This "fixes" a number of programs that are currently
        incorrect because they define either a copy constructor
        or destrutor, but do not define assignment.  In almost
        all cases, the new default will give the correct
        behavior. It works for classes that allocate and
        deallocate resources, manage reference counts, and those
        that simply allocate and delete memory.

    2.  Many classes can now be written without explicitly
        defining the assignment operator.  This include classes
        that manage resources, such as opening files, and those
        that manage reference counts.

    3.  This would make C++ easier to teach, because it would be
        possible to defer discussion of assignment to a more
        advanced course.  Currently, operator=() overloading has
        to be taught relatively soon, since writing assignment is
        necessary to write a properly functioning class that
        allocates and deallocates storage.

The only programs that are affected by the change change are
those that have a copy constructor or destructor AND do not
define an assignment operator.

Programs that have either a copy constructor or a destructor but
do not define assignment are usually incorrect. In such programs,
the semantics of construction or destruction probably differ from
those of the copy of new contents and cleanup of the existing
contents during assignment. This is the basis for the claim of
upward compatibility forcorrect programs.

To reduce the impact of this change, it is proposed that in the
case that the copy constructor, destructor and assignment are
all generated, this definition leaves the standard unchanged.
It is believe that this is not significantly different that
executing the destructor followed by the constructor.  However,
the change might cause problems with some programs.



Example:

   class B {
     public:
        B(const B&);
        ~B();
    };

   class M1 {
     public:
        M1(const M1&);
        ~M1();
   };

   class M2 {
     public:
        M2(const M2&);
        ~M2();
   };

   class DwCC : public B
   {
      public:
     DwCC();
        DwCC(const DwCC& src) : B(src), m1(src.m1), m2(src.m2) {}
        M1 m1;
        M2 m2;
      private:
   };

  class DwoCC: public B
  {
    public:
    DwoCC();
       M1 m1;
       M2 m2;
  };


Currently, these two classes will generate the same default
assignment operator.  With the proposed change, the default
assignment operator for DwCC change from:

        m1 = src.m1;
        m2 = src.m2;

        This executes as:

                ~m1();
                m1(src.m1);
                ~m2();
                m2(src.m2);

With the change, class DwCC would have its assignment operator
generate


      ~DwCC()
       new (this) DwCC(src);

        This executes as:

                ~m2();
                ~m1();
                m1(m1.src);
                m2(m2.src);
---
[ comp.std.c++ is moderated.  Submission address: std-c++@ncar.ucar.edu.
  Contact address: std-c++-request@ncar.ucar.edu.  The moderation policy
  is summarized in http://dogbert.lbl.gov/~matt/std-c++/policy.html. ]