[comp.lang.c++] Copying objects

mccarthy@well.sf.ca.us (Patrick McCarthy) (06/27/90)

Given a pointer to a base type, what is the best way to copy the object
pointed to (bearing in mind that it may actually point to an object of a
derived type)?
 
In other words, suppose you have a class called Base, and two classes
Derived_1 and Derived_2 which inherit from Base.  Suppose further that p
and q are of type Base *.  In order to make p point to a copy of q using
q's copy constructor, you would have know q's type in advance as
follows:
 
    p = (Base *) new Derived_2(q);
 
I am searching for a solution which does not require knowlege of the
type of the object being copied.
 
The best way I can think of to do this is to define a virtual function,
Base *Base::CloneSelf(), in the base class.  This function would then be
overridden by all inherited classes.  The wierdism here is that in order
to override the function, Derived_1::CloneSelf() would have to return
a pointer of type Base *.
 
It seems as though there should be a better way of doing this.	Any and
all suggestions are welcome (as long as they're _constructive_ - pun
intended :-)
 
Thanks!
 
------------------------------------------------------------------------
Pat McCarthy
mccarthy@well.uucp
 
".signature? I ain't got to show you no stinking .signature!"

dsr@luke.mitre.org (Douglas S. Rand) (06/27/90)

Copying objects is lots of fun.  You can consider a few problems/options.  

1)  Do subobject pointers get copied?  or do the subobjects get duplicated and
    the new pointer assigned?

2)  How do you handle method combination?  Is each class responsible for
the entire
    copy or does a child class ask its parent to copy the parent's data
(note that this
    involves the extra bookeeping of explicitly calling the parent
method (as AT&T won't
    admit that a symbol indicating 'super' is worth it).

3)  Do you really want an exact copy?  Maybe some instance vars need to
be individual 
    (like numerical identifiers).

etc. etc. etc.

In any case,  use a virtual function for each class in the hierarchy and
use a consistent
style.  You probably don't want to just duplicate the object.  This is, 
BTW,  why C++
doesn't provide a simple copy function (neither does CLOS in CommonLISP,
and I 
can't remember whether St80 does).

Douglas S. Rand 
Internet:   <dsrand@mitre.org>
Snail:	    MITRE, Burlington Road, Bedford, MA 
Disclaimer: MITRE might agree with me - then again...

kgorlen@sparkler.dcrt.nih.gov (Keith Gorlen) (06/28/90)

In article <18753@well.sf.ca.us>, mccarthy@well.sf.ca.us (Patrick
McCarthy) writes:
|>Given a pointer to a base type, what is the best way to copy the object
|>pointed to (bearing in mind that it may actually point to an object of a
|>derived type)?

You might take a look at how the NIH Class Library implements the
shallowCopy() and deepCopy() member functions.  Basically, each class X
defines the virtual function

	Object* X::shallowCopy() const	{ return new X(*this); }

which will create a shallow copy of an X object by using the copy
constructor for class X.

Object::deepCopy() is a non-virtual member function that first uses
shallowCopy() to make a shallow copy of an object, then invokes the
virtual member function deepenShallowCopy().  Each class that has member
variables that are pointers to objects or class instances implements
deepenShallowCopy() to convert the shallow copy to a deep copy by
applying deepenShallowCopy() recursively to the pointers and objects it
contains.

All this is described in detail in Section 7.3 of our new book "Data
Abstraction and Object-Oriented Programming in C++", John Wiley & Sons,
ISBN 047192346X.  (I understand the first copies are probably running
off the presses as I type.)
                                
The software is available via anonymous FTP in the file pub/nihcl.tar.Z
on host alw.nih.gov (128.231.128.251).

	Keith Gorlen			phone: (301) 496-1111
	Building 12A, Room 2033		uucp: uunet!nih-csl!kgorlen
	National Institutes of Health	Internet: kgorlen@alw.nih.gov
	Bethesda, MD 20892

ericg@ucschu.ucsc.edu (Eric Goodman) (07/03/90)

In article <18753@well.sf.ca.us> mccarthy@well.sf.ca.us (Patrick McCarthy) 
writes:
> In other words, suppose you have a class called Base, and two classes
> Derived_1 and Derived_2 which inherit from Base.  Suppose further that p
> and q are of type Base *.  In order to make p point to a copy of q using
> q's copy constructor, you would have know q's type in advance as
> follows:

A virtual overloaded operator= should work (I'm assuming that virtual 
operators are legal); 
the problem is that you would have to define it backwards:

Class Base{
public:
virtual Base operator=(Base) { ... }
};

insures that the left operand of the = is of the correct type, so
*p=*q; // could be used to copy *p into *q with p's type insured.  
I don't see how you could do this to copy in the ordinary fashion, though 
so this may be useless.

Eric Goodman
Humanities Division
University of California, Santa Cruz
ericg@ucschu.ucsc.edu
ericg@ucschu.bitnet
Eric_Goodman.staff@macmail.ucsc.edu
...!ucbvax!ucscc!ucschu!ericg

jimad@microsoft.UUCP (Jim ADCOCK) (07/04/90)

In article <4853@darkstar.ucsc.edu> ericg@ucschu.ucsc.edu (Eric Goodman) writes:
>A virtual overloaded operator= should work (I'm assuming that virtual 
>operators are legal); 

Virtual overloaded operators are legal -- except operators new and delete.
This is all neatly summerized in a table on page 306 of ARM.

Interestingly, operator= has the unique characteristic of being
virtual but not inherited!  Can someone explain what this is suppose
to mean? -- When I try the example below on my local compilers, it appears
that virtual B& B::operator=(const B&) *is* being inherited. ???

extern "C"
{
#include "stdio.h"
}

class B
{
public:
	virtual B& operator=(const B&);
};
B& B::operator=(const B&) 
{ static int i=0; printf("%lX B::op=\n", (long)&i); return *this; }

class D : public B
{
public:
	int i;
};

main()
{
	B& b1 = *new B;
	B& b2 = *new B;
	D& d1 = *new D;
	D& d2 = *new D;
	b1 = b1;
	d1 = d2;
	b1 = d1;
}

steve@taumet.com (Stephen Clamage) (07/05/90)

In article <55612@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
>Interestingly, operator= has the unique characteristic of being
>virtual but not inherited!  Can someone explain what this is suppose
>to mean?

If a derived class inherits an operator=, what happens to the extra
fields in the derived class?  If the programmer goes to the trouble
of writing operator=, the compiler must assume that something special,
beyond memberwise assignment, is required.  The compiler cannot guess
what this is, so leaves it to the programmer.  Hence, operator= is not
inherited.  (We could argue that if the derived class has no data
members it is OK to inherit the operator=, but it is simpler to go
with the ARM and say it is not inherited.)

Virtual is a separate concept, and a virtual operator= merely assures
that the correct operator is called in the case of polymorphic objects.

The ARM states that the default operator= (memberwise assignment) is
generated if (among other things) its address is taken.  If a derived
class needs a virtual operator= and the programmer does not declare one,
the compiler should generate the default version and put it in the
virtual table.  If this didn't happen, it may be a bug in the compiler,
or a compiler written to an earlier version of C++.
-- 

Steve Clamage, TauMetric Corp, steve@taumet.com

jimad@microsoft.UUCP (Jim ADCOCK) (07/05/90)

In article <304@taumet.com> steve@taumet.UUCP (Stephen Clamage) writes:
|In article <55612@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes:
|>Interestingly, operator= has the unique characteristic of being
|>virtual but not inherited!  Can someone explain what this is suppose
|>to mean?
....
|The ARM states that the default operator= (memberwise assignment) is
|generated if (among other things) its address is taken.  If a derived
|class needs a virtual operator= and the programmer does not declare one,
|the compiler should generate the default version and put it in the
|virtual table.  If this didn't happen, it may be a bug in the compiler,
|or a compiler written to an earlier version of C++.

Look at the example again.  The compiler should only generate a default
operator= of the form D& D::operator=([const] D&).  But the virtual 
function whose template to be matched is B& D::operator=(const B&).
So presumably, a compiler should, in the special case of virtual 
operator=, give a compile time squawk if B& D::operator=(const B&)
isn't explicitly defined in every subclass D of B ?

[The compiler I tried this example on erroneously allowed D to inherit
B& B::operator=(const B&) via the vtable]

Still doesn't answer my question though -- what is this suppose to *mean* ???