[comp.lang.c++] Need `set before using' warning

cline@cheetah.ece.clarkson.edu (Marshall Cline) (10/06/90)

In article <11745@cadillac.CAD.MCC.COM> vaughan@mcc.com (Paul Vaughan) writes:
>I've been tending to use the initialization form more and more, but I
>don't really have a good reason for why I should do that.  A simple
>argument against using the initialization form is that you have to
>initialize members for each constructor, where assignments are
>conveniently swept into an init function called by each constructor.
>And as is pointed out above, you have to decide first whether it's
>even feasible.  So, how do you (anybody who is reading this) justify
>this opinion?

The example I give in my C++/OOD course is as follows (this is similar
Stan Lippman's example [Lippman/C++ Primer/Addison-Wesley]).
----------------------------------------------------------------------------
class String {
  int len;
  char* data;
public:
  String();			// Make a zero-len String
  String(const char* s);	// char* --> String
  String(const String& s);	// String copy ctor
  String& operator=(const String& s);  //String assignment
};

class Name { /*...*/
  String s;
public:
  Name(const char* S) { s = S; }
};

main()
{
  Name neighbor = "Joe";	// How expensive is this?????????
}
----------------------------------------------------------------------------
The innocent looking `neighbor' object is terribly costly to construct:
[1] Name::Name(const char*) gets called with parameter "Joe".
[2] Name::Name(const char*) has no init list, so member object
    ``neighbor.s'' gets constructed by the default ctor String::String().
    This will probably allocate a 1 byte area from freestore for the '\0'.
[3] A temporary "Joe" String is created using String::String(const char*).
    This is another freestore allocation and a strcpy().
[4] String::operator=(const String&) is called with the temporary String.
    This will `delete' the old string in `s', use another new to get
    space for the new string, and do another strcpy().
[5] The temporary String gets destroyed, yet another freestore operation.

Final score: 3 `new', 2 `strcpy()', and 2 `delete'
Total ``cost units'': 7
----------------------------------------------------------------------------
Compare this to an initialization-list version:
Simply replace:	Name::Name(const char* S) { s = S; }
with:		Name::Name(const char* S) s(S) { }

Now construction of `neighbor' is:

[1] Name::Name(const char*) gets called with parameter "Joe"
[2] Name::Name(const char*) *has* an init list, so ``neighbor::s'' is
    initialized from `S' with String::String(const char*).
[3] String::String("Joe") will probably do a `new' and a `strcpy()'.

Final score: 1 `new', 1 `strcpy()', and 0 `delete'
Total ``cost units'': 2
----------------------------------------------------------------------------

I concur with the poster that said to *always* use the initialization
syntax (always, that is, except when you can't :-).  Stan's book gives an
example similar to the above, but he generally uses the assignment syntax
whenever it doesn't matter (ex: for builtin types, etc).  It seems easier
to me to always use initialization, so if you change `char*' to `String',
for example, you don't have to remember to change your constructors too.

Marshall Cline

--
PS: If your company is interested in on-site C++/OOD training, drop me a line!
PPS: Career search in progress; ECE faculty; research oriented; will send vita.
--
Marshall Cline / Asst.Prof / ECE Dept / Clarkson Univ / Potsdam, NY 13676
cline@sun.soe.clarkson.edu / Bitnet:BH0W@CLUTX / uunet!clutx.clarkson.edu!bh0w
Voice: 315-268-3868 / Secretary: 315-268-6511 / FAX: 315-268-7600