Home arrow C++ Programming arrow Operator Overloading

Language Translator

Hacking Zone

Hacking Tools
Attacking

Configure Windows

Windows Configuration

Novels

Mix Novels

Human Personality

Body Language
Operator Overloading PDF Print E-mail
Written by Hemanshu Patel   
Monday, 22 October 2007
Article Index
Operator Overloading
Page 2
Page 3
Page 4
Page 5
Page 6
Page 7
Page 8
Page 9

Automatic type conversion

In C and C++, if the compiler sees an expression or function call using a type that isn’t quite the one it needs, it can often perform an automatic type conversion from the type it has to the type it wants. In C++, you can achieve this same effect for user-defined types by defining automatic type conversion functions. These functions come in two flavors: a particular type of constructor and an overloaded operator.

 

Constructor conversion

If you define a constructor that takes as its single argument an object (or reference) of another type, that constructor allows the compiler to perform an automatic type conversion. For example,

//: C12:AutomaticTypeConversion.cpp
// Type conversion constructor
class One {
public:
One() {}
};

class Two {
public:
Two(const One&) {}
};

void f(Two) {}

int main() {
One one;
f(one); // Wants a Two, has a One
} ///:~

When the compiler sees f( ) called with a One object, it looks at the declaration for f( ) and notices it wants a Two. Then it looks to see if there’s any way to get a Two from a One, and it finds the constructor Two::Two(One), which it quietly calls. The resulting Two object is handed to f( ).

In this case, automatic type conversion has saved you from the trouble of defining two overloaded versions of f( ). However, the cost is the hidden constructor call to Two, which may matter if you’re concerned about the efficiency of calls to f( ).

 

Preventing constructor conversion

There are times when automatic type conversion via the constructor can cause problems. To turn it off, you modify the constructor by prefacing with the keyword explicit (which only works with constructors). Used to modify the constructor of class Two in the example above:

//: C12:ExplicitKeyword.cpp
// Using the "explicit" keyword
class One {
public:
One() {}
};

class Two {
public:
explicit Two(const One&) {}
};

void f(Two) {}

int main() {
One one;
//! f(one); // No auto conversion allowed
f(Two(one)); // OK -- user performs conversion
} ///:~

By making Two’s constructor explicit, the compiler is told not to perform any automatic conversion using that particular constructor (other non-explicit constructors in that class can still perform automatic conversions). If the user wants to make the conversion happen, the code must be written out. In the code above, f(Two(one)) creates a temporary object of type Two from one, just like the compiler did in the previous version.

 

Operator conversion

The second way to produce automatic type conversion is through operator overloading. You can create a member function that takes the current type and converts it to the desired type using the operator keyword followed by the type you want to convert to. This form of operator overloading is unique because you don’t appear to specify a return type – the return type is the name of the operator you’re overloading. Here’s an example:

//: C12:OperatorOverloadingConversion.cpp
class Three {
int i;
public:
Three(int ii = 0, int = 0) : i(ii) {}
};

class Four {
int x;
public:
Four(int xx) : x(xx) {}
operator Three() const { return Three(x); }
};

void g(Three) {}

int main() {
Four four(1);
g(four);
g(1); // Calls Three(1,0)
} ///:~

With the constructor technique, the destination class is performing the conversion, but with operators, the source class performs the conversion. The value of the constructor technique is that you can add a new conversion path to an existing system as you’re creating a new class. However, creating a single-argument constructor always defines an automatic type conversion (even if it’s got more than one argument, if the rest of the arguments are defaulted), which may not be what you want (in which case you can turn it off using explicit). In addition, there’s no way to use a constructor conversion from a user-defined type to a built-in type; this is possible only with operator overloading.

 

Reflexivity

One of the most convenient reasons to use global overloaded operators instead of member operators is that in the global versions, automatic type conversion may be applied to either operand, whereas with member objects, the left-hand operand must already be the proper type. If you want both operands to be converted, the global versions can save a lot of coding. Here’s a small example:

//: C12:ReflexivityInOverloading.cpp
class Number {
int i;
public:
Number(int ii = 0) : i(ii) {}
const Number
operator+(const Number& n) const {
return Number(i + n.i);
}
friend const Number
operator-(const Number&, const Number&);
};

const Number
operator-(const Number& n1,
const Number& n2) {
return Number(n1.i - n2.i);
}

int main() {
Number a(47), b(11);
a + b; // OK
a + 1; // 2nd arg converted to Number
//! 1 + a; // Wrong! 1st arg not of type Number
a - b; // OK
a - 1; // 2nd arg converted to Number
1 - a; // 1st arg converted to Number
} ///:~

Class Number has both a member operator+ and a friend operator–. Because there’s a constructor that takes a single int argument, an int can be automatically converted to a Number, but only under the right conditions. In main( ), you can see that adding a Number to another Number works fine because it’s an exact match to the overloaded operator. Also, when the compiler sees a Number followed by a + and an int, it can match to the member function Number::operator+ and convert the int argument to a Number using the constructor. But when it sees an int, a +, and a Number, it doesn’t know what to do because all it has is Number::operator+, which requires that the left operand already be a Number object. Thus, the compiler issues an error.

With the friend operator–, things are different. The compiler needs to fill in both its arguments however it can; it isn’t restricted to having a Number as the left-hand argument. Thus, if it sees

1 – a

it can convert the first argument to a Number using the constructor.

Sometimes you want to be able to restrict the use of your operators by making them members. For example, when multiplying a matrix by a vector, the vector must go on the right. But if you want your operators to be able to convert either argument, make the operator a friend function.

Fortunately, the compiler will not take 1 – 1 and convert both arguments to Number objects and then call operator–. That would mean that existing C code might suddenly start to work differently. The compiler matches the “simplest” possibility first, which is the built-in operator for the expression 1 – 1.

 

Type conversion example

An example in which automatic type conversion is extremely helpful occurs with any class that encapsulates character strings (in this case, we will just implement the class using the Standard C++ string class because it’s simple). Without automatic type conversion, if you want to use all the existing string functions from the Standard C library, you have to create a member function for each one, like this:

//: C12:Strings1.cpp
// No auto type conversion
#include "../require.h"
#include <cstring>
#include <cstdlib>
#include <string>
using namespace std;

class Stringc {
string s;
public:
Stringc(const string& str = "") : s(str) {}
int strcmp(const Stringc& S) const {
return ::strcmp(s.c_str(), S.s.c_str());
}
// ... etc., for every function in string.h
};

int main() {
Stringc s1("hello"), s2("there");
s1.strcmp(s2);
} ///:~

Here, only the strcmp( ) function is created, but you’d have to create a corresponding function for every one in <cstring> that might be needed. Fortunately, you can provide an automatic type conversion allowing access to all the functions in <cstring>:

//: C12:Strings2.cpp
// With auto type conversion
#include "../require.h"
#include <cstring>
#include <cstdlib>
#include <string>
using namespace std;

class Stringc {
string s;
public:
Stringc(const string& str = "") : s(str) {}
operator const char*() const {
return s.c_str();
}
};

int main() {
Stringc s1("hello"), s2("there");
strcmp(s1, s2); // Standard C function
strspn(s1, s2); // Any string function!
} ///:~

Now any function that takes a char* argument can also take a Stringc argument because the compiler knows how to make a char* from a Stringc.



 
< Prev   Next >
Your Ad Here

Donate us!!

Enter Amount:

RSS socialnet

Add to MyYahoo!
Subscribe in NewsGator Online
Add to Newsburst
Add to Google
Add to My AOL
Add to Pluck
Subscribe in FeedLounge
Add to Windows Live
Add to NetVibes
Subscribe in Rojo
Subscribe in Bloglines
Add to MyMSN
Add to Plusmo for your cellphone
Add to PageFlakes
Add to Technorati
Add to BlinkBits