Path: utzoo!utgpu!news-server.csri.toronto.edu!mailrus!tut.cis.ohio-state.edu!zaphod.mps.ohio-state.edu!wuarchive!uunet!microsoft!jimad From: jimad@microsoft.UUCP (Jim ADCOCK) Newsgroups: comp.lang.c++ Subject: Re: reference parameters for overloaded operators Message-ID: <56873@microsoft.UUCP> Date: 23 Aug 90 21:32:29 GMT References: <1990Aug13.144540.7804@ctk1.UUCP> <26CBE285.4EC2@tct.uucp> <56749@microsoft.UUCP> <117703@linus.mitre.org> Reply-To: jimad@microsoft.UUCP (Jim ADCOCK) Organization: Microsoft Corp., Redmond WA Lines: 86 In article <117703@linus.mitre.org> fkuhl@ralph.mitre.org (F. S. Kuhl) writes: >In article <56749@microsoft.UUCP> jimad@microsoft.UUCP (Jim ADCOCK) writes: >>Also, whenever doing polymorphic programming [as in vtables and virtual >>functions, not polymophic via parameter overloading] one should call >>by reference to avoid the slicing problem. > >OK, I give. What's the slicing problem? C++ already looks to my >uninitiated mind like a Swiss Army knife. Well, its certainly true that C++ has three main blades to stick you with when it comes to pass parameters: pass-by-value pass-by-reference pass-by-pointer Pass-by-value is as in C. Pass-by-pointer is as in C, where a pointer to an object is passed to allow it to be modified -- although there are other good reasons to want to do this in C++ in addition to the historical C reasons. Pass-by-reference is as in Pascal pass-by-reference, and is typically the default in most other OOPL languages -- whereas by default C++ is pass-by-value. The 'Slicing' problem arises in C++ when some "thing2" is passed by value as a parameter -- where the exact type of "thing2" is not known, because the "thing2" is actually a derivative of some base class "thing." But, in deriving thing2, more members are typically added to thing2 than are in a base class thing. Passing thing2 by value, as-if it were a "thing", causes those extra members not to be copied -- they are "sliced off." This generally leads to undesirable behavior. The solution is simple, once recognized: "never" use pass-by-value where you'd expect polymorphic behavior. Use [const] references instead. [This makes the 'slicing problem' a quick and easy test of new "C++" books you find at your store -- If an author is talking about "polymorphic this" and "polymorphic that," but all his/her "C++" examples use pass-by-value parameters -- Well, then the best one can say of such authors is that they don't know C++ !!!] ....In the following code, PrintWith_version2 suffers the slicing problem. PrintWith_version1 avoids the problem by passing by reference -- effectively allowing different sized objects to be used as its parameter. extern "C" { #include } class THING { private: const char* name; public: THING(const char* nameT) : name(nameT) {} virtual void Print() const; virtual void PrintWith_version1(const THING& thing) const; virtual void PrintWith_version2(THING thing) const; }; void THING::Print() const { printf("%s ", name); } void THING::PrintWith_version1(const THING& thing) const { Print(); thing.Print(); putchar('\n'); } void THING::PrintWith_version2(THING thing) const { Print(); thing.Print(); putchar('\n'); } class THING2 : public THING { private: const char* name2; public: THING2(const char* name1T, const char* name2T) : THING(name1T), name2(name2T) {} virtual void Print() const; }; void THING2::Print() const { THING::Print(); printf("%s ", name2); } void main() { THING& thing1 = *new THING("thing1_name"); THING& thing2 = *new THING2("thing2_firstname", "thing2_lastname"); thing1.Print(); putchar('\n'); thing2.Print(); putchar('\n'); thing1.PrintWith_version1(thing2); thing1.PrintWith_version2(thing2); }