Columns


Questions & Answers

C++ Overloaded Constructors and Operators

Kenneth Pugh


Kenneth Pugh, a principal in Pugh-Killeen Associates, teaches C and C++ language courses for corporations. He is the author of All On C, C for COBOL Programmers, and UNIX for MS-DOS Users, and was a member of the ANSI C committee. He also does custom C/C++ programming and provides SystemArchitectonicssm services. His address is 4201 University Dr., Suite 102, Durham, NC 27707. You may fax questions for Ken to (919) 489-5239. Ken also receives email at kpugh@allen.com (Internet) and on Compuserve 70125,1142.

Q

Thank you very much for your excellent articles in C/C++ Users Journal, I look forward to them each month. As a beginner to the C++ language I would like to ask three questions on the subject of overloading operators and constructors.

It seems all the books I have read on the subject of operator overloading explain it as if it can only happen once in a class, and never give examples of overloading the same operators twice. So here's my questions:

1. Is it possible to overload an operator twice in the same class, once for private, and once for public data?

2. Is it possible to overload a constructor in the same class, once for private, and once for public data?

3. How do I avoid this (kludgy) approach in trying to accomplish number 2 above:

I create a second constructor for the public data by adding a variable that is never used (see Listing 1) . It works but it may cause readability problems later on.

Hans Wedemeyer

A

In answer to your first question, you can only overload an operator once for each pair of left-hand/right-hand arguments. For example, in a String class, you can create three different functions for the + operator if it's called with three different pairs of argument types, such as those shown in the following code fragment:

   String string1, string2, string3;
   int i;
   string1 = string2 + string3;
   // pair #1 string + string
   string1 = string2 + i;
   // pair #2 string + int
   string1 = i + string2;
   // pair #3 int + string
In the first and second cases, the operator function can be a member of class String, as in:

   class String
      {
   public:
      String operator+(String &
                    other_string);
      String operator+(int an_int);
      //...
      };
In the third case, you must define the operator function as a non-member function, since you cannot add it to the built-in int class. The prototype for this function would look like this:

   String operator+(String one_string,
                 String another_string);
You can define the first two cases either as member functions or as non-member functions, but not as both. It is up to you to define the meanings for each of these three + operators. For the user's sake, they should have similar meanings. For example, if the first form means concatenation of other_string to the object, then the other forms could mean convert the int to a String and then concatenate that String to the other String.

Just as with non-operator functions, you cannot define another operator function with the same name and the same parameter types (i.e., with the same signature) which differs only in the return type. For example, if you define an operator+ function as above, then you cannot create another operator+ that looks like this:

   int operator+(String one_string,
               String another_string);
As far as your second question goes, I'm not sure what your ultimate purpose is for this class. The rule of thumb is that an object of a class should be constructed with reasonable initial values established for all of its data members. Or at the very least, the object should know the status (valid, invalid) of its members. In your example, it appears you want the constructor to initialize one of two members in a class and leave the other alone. The fact that one is public and one is private is irrelevant. You would face a similar problem if both members were private.

The approach you have taken will work, but does not appear optimal. I would suggest something along the lines of:

   class ClassOne
       {
   public:
       ClassOne(int which_one, char * a_string);
       char second_string[35];
   private:
       char string[35];
       ...
       };
   ClassOne::ClassOne(int which_one, char * a_string)
       {
       switch(which_one)
            {
       case 0:
            strcpy(string, a_string);
            strcpy(second_string,"");
            break;
       case 1:
            strcpy(second_string, a_string);
            strcpy(string, "");
            break;
       default:
            // Throw exception, report it
            // or whatever you like.
            };
       }
The user of this class would call it with:

   calling_function()
      {
      ClassOne c1(0,"hello"), c2(1,"hello");
      //...
      }
Alternatively, the constructor could initialize just the private data member. The initialization of the public member could be left to the user, since it is accessible.

Whenever a switch statement appears in a function as above, you should explore the use of inheritance. Perhaps you have two classes intertwined in one. In the example you gave, you might consider having a base class that is inherited by two classes.

   class BaseClass
       {
   public:
       //.. Any desired functions
   protected:
       char second_string[35];
       char string[35];
   private:
       BaseClass();   // Nobody can create one of these
       ...
       };

   class ClassOne::public BaseClass
       {
       ClassOne(char *a_string);
       };

   class ClassTwo::public BaseClass
       {
       ClassTwo(char * a_string);
       };

   ClassOne::ClassOne(char * a_string)
       {
       strcpy(string, a_string);
       strcpy(second_string,"");
       }

   ClassTwo:ClassTwo(char * a_string)
       {
       strcpy(second_string, a_string);
       strcpy(string, "");
       }
The derived class type of an object will determine which string is initialized with the parameter in the constructor. For example, a caller might use:

   calling_function()
       {
       ClassOne c1("hello"), c2("hello");
       //...
       }
It appears you would like to have the ostream operator << output either string or string_two, depending on what is initialized. You can set up the overloaded operator to use a switch statement, as shown in the constructor above. In that case, you should put a data member in the class to save the value of which_one that is passed at construction time. If you use inheritance, then you can overload operator <<for both ClassOne and ClassTwo.

DO loops and braces

Q

I find your C style of brace-alignment difficult to read. With a little practice, I did finally see your point about alignment [see"Moving from Fortran to C," CUJ, June 1994, pp. 75-78].

But, aren't you obfuscating structure that is more simple than it looks? Would you build a physical structure out of oddly shaped boards? Why not use a "linear" structure whenever possible?

I suggest the following is a linear (read "less complex") structure:

   for (   )
   {
   }
Your "non-linear" (read "more complex") structure

   for (   )
       {
       }
cannot be quickly scanned, especially when for loops are nested. The braces can line up (a linear model) with the wrong control structure!

Earl F. Glynn
Lenexa, KS

A

Well, I have found a fairly even split between the "innies" and the "outies," with the innies perhaps being in a small majority. The outies are the ones who prefer, as you do, the form:

   for (   )
   {
      statements
   }
Being an innie, I have a strong preference for indented braces, as in:

   for(   )
      {
      statements
      }
This style is consistent with the style typically used for a single statement, as in:

   for (  )
       statement
I think this style is more "linear," since the braces line up with the statements they enclose. I guess we could philosophize for months as to whether the braces are part of the structure which is controlled or whether they simply enclose the controlled structure. (Are walls part of the room?).

Interestingly enough, very few programmers I meet today appear to use the K & R style:

   for (  ) {

      statements
      }
Luckily, we can run code we do not like through a "beautify" program and it will come out looking the way we want.