Secondly, a conversion operator takes no arguments.Conversion operators can convert their object to any given type, fundamental and user-defined alike: struct DateRep //legacy C code //
Trang 1Secondly, a conversion operator takes no arguments.
Conversion operators can convert their object to any given type, fundamental and user-defined alike:
struct DateRep //legacy C code
//transmit date object as a binary stream to a remote client
int ret_stat = transmit_date; //using legacy communication API
return 0;
}
Standard Versus User-Defined Conversions
The interaction of a user-defined conversion with a standard conversion can cause undesirable surprises and sideeffects, and therefore must be used with caution Examine the following concrete example
A non-explicit constructor that takes a single argument is also a conversion operator, which casts its argument to
an object of this class When the compiler has to resolve an overloaded function call, it takes into consideration suchuser-defined conversions in addition to the standard ones For example
f(5.f); //OK, calls void f(Numeric) Numeric's constructor
//converts argument to a Numeric object
'Suppose you add, at a later stage, another overloaded version of f():
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 3 - Operator Overloading
file:///D|/Cool Stuff/old/ftp/1/1/ch03/ch03.htm (8 von 15) [12.05.2000 14:45:47]
Trang 2void f (double);
Now the same function call resolves differently:
f(5.f); // now calls f(double), not f(Numeric)
This is because float is promoted to double automatically in order to match an overloaded function signature.This is a standard type conversion On the other hand, the conversion of float to Numeric is a user-defined
conversion User-defined conversions rank lower than standard conversions -in overload resolution; as a result, thefunction call resolves differently
Because of this phenomenon and others, conversion operators have been severely criticized Some programmingschools ban their usage altogether However, conversion operators are a valuable and sometimes inevitable toolfor bridging between dual interfaces, as you have seen
Postfix and Prefix Operators
For primitive types, C++ distinguishes between ++x; and x++; as well as between x; and x ; Under somecircumstances, objects have to distinguish between prefix and postfix overloaded operators as well (for example, as anoptimization measure See Chapter 12, "Optimizing Your Code") Postfix operators are declared with a dummy intargument, whereas their prefix counterparts take no arguments For example
class Date
{
public:
Date& operator++(); //prefix
Date& operator (); //prefix
Date& operator++(int unused); //postfix
Date& operator (int unused); //postfix
};
void f()
{
Date d, d1;
d1 = ++d;//prefix: first increment d and then assign to d1
d1 = d++; //postfix; first assign, increment d afterwards
}
Using Function Call Syntax
An overloaded operator call is merely "syntactic sugar" for an ordinary function call You can use the explicit
function call instead of the operator syntax as follows:
bool operator==(const Date& d1, const Date& d2);
void f()
{
Date d, d1;
bool equal;
d1.operator++(0); // equivalent to: d1++;
d1.operator++(); // equivalent to: ++d1;
equal = operator==(d, d1);// equivalent to: d==d1;
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 3 - Operator Overloading
file:///D|/Cool Stuff/old/ftp/1/1/ch03/ch03.htm (9 von 15) [12.05.2000 14:45:47]
Trang 3Date&(Date::*pmf) (); //pointer to member function
pmf = & Date::operator++;
}
Consistent Operator Overloading
Whenever you overload operators such as + or -, it might become necessary to support the corresponding += and -=operators as well The compiler does not do that for you automatically Consider the following example:
d1 = d1 + d2; //fine; uses overloaded + and default assignment operator
d1 += d2; //compile time error: 'no user defined operator += for class Date'Theoretically, the compiler could synthesize a compound operator += by combing the assignment operator and theoverloaded + operator so that the expression d1 += d2; is automatically expanded into d1 = d1+d2;
However, this is undesirable because the automatic expansion might be less efficient than a user-defined version ofthe operator An automated version creates a temporary object, whereas a user-defined version can avoid it
Moreover, it is not difficult to think of situations in which a class has an overloaded operator +, but does not haveoperator += intentionally
Returning Objects by Value
For the sake of efficiency, large objects are usually passed to or returned from a function by reference or by theiraddress There are, however, a few circumstances in which the best choice is still to return an object by value
Operator + is an example of this situation It has to return a result object, but it cannot modify any of its operands Theseemingly natural choice is to allocate the result object on the free store and return its address Nevertheless, this isnot such a good idea Dynamic memory allocation is significantly slower than local storage It also might fail andthrow an exception, which then has to be caught and handled Even worse, this solution is error prone because it isunclear who is responsible for deleting this object the creator or the user?
Another solution is to use a static object and return it by reference For example
Year& operator + (const Year& other) const; //returns a reference to
//a local static Year
int getYear() const;
void setYear(int y);
};
Year& Year::operator + (const Year& other) const
{
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 3 - Operator Overloading
file:///D|/Cool Stuff/old/ftp/1/1/ch03/ch03.htm (10 von 15) [12.05.2000 14:45:47]
Trang 4static Year result;
result = Year(this->getYear() + other.getYear() );
return result;
}
Static objects solve the ownership problem, but they are still problematic: On each invocation of the overloadedoperator, the same instance of the static object is being modified and returned to the caller The same object can bereturned by reference to several more users, who do not know that they are holding a shared instance that has justbeen modified behind their back
Finally, the safest and most efficient solution is still to return the result object by value:
bool operator == (const Month& m1, const Month &m2);
It is possible to use the overloaded operator == to compare a plain int value and a Month object due to the implicitconversion of int to Month For example
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 3 - Operator Overloading
file:///D|/Cool Stuff/old/ftp/1/1/ch03/ch03.htm (11 von 15) [12.05.2000 14:45:47]
Trang 5This works fine, but it i's inefficient: The argument n is first converted to a temporary Month object, which is thencompared with the object June You can avoid the unnecessary construction of a temporary object by definingadditional overloaded versions of operator ==:
bool operator == (int m, const Month& month);
bool operator == (const Month& month, int m);
Consequently, the expression June == n will now invoke the following overloaded operator:
bool operator == (const Month& month, int m);
This overloaded version does not create a temporary object, so it's more efficient The same performance
considerations led the C++ Standardization committee to define three distinct versions of operator == for
std::string (see Chapter 10, ""STL and Generic Programming"") and other classes of the Standard Library.'
Overloading Operators for Other User-Defined types
You can overload an operator for enum types as well For example, it may be useful to overload operators such as ++and so that they can iterate through the enumerator values of a given enum type You can do it like this:
return d = Monday; //rollover
int temp = d; //convert to an int
return d = static_cast<Days> (++temp);
}
int main()
{
Days day = Monday;
for (;;) //display days as integers
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 3 - Operator Overloading
file:///D|/Cool Stuff/old/ftp/1/1/ch03/ch03.htm (12 von 15) [12.05.2000 14:45:47]
Trang 6Overloading the Subscripts Operator
For various classes that contain arrays of elements, it's handy to overload the subscript operator to access a singleelement Remember always to define two versions of the subscript operator: a const version and a non-constversion For example
void * operator [] (int index) { return ptr_array[index];}
const void * operator [] (int index) const { return ptr_array[index];}};
void f(const Ptr_collection & pointers)
{
const void *p = pointers[0]; //calls const version of operator []
if ( p == 0)
return;
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 3 - Operator Overloading
file:///D|/Cool Stuff/old/ftp/1/1/ch03/ch03.htm (13 von 15) [12.05.2000 14:45:47]
Trang 7#include <iostream>
using namespace std;
class increment
{
//a generic increment function
public : template < class T > T operator() (T t) const { return ++t;}
associativity and the precedence of an operator cannot be altered Built-in operators have an interface, consisting ofthe number of operands to which the operator can be applied, whether the operator modifies any of its operands, andresult that is returned by the operator When you are overloading an operator, it is recommended that you conform toits built-in interface
Conversion operators are a special type of overloaded operators They differ from ordinary overloaded operators intwo respects: They do not have a return value and they do not take any arguments
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 3 - Operator Overloading
file:///D|/Cool Stuff/old/ftp/1/1/ch03/ch03.htm (14 von 15) [12.05.2000 14:45:47]
Trang 9ANSI/ISO C++ Professional Programmer's
Handbook
Contents
4 Special Member Functions: Default Constructor, Copy Constructor, Destructor,
And Assignment Operator
Simulating Virtual Constructors
Covariance of Virtual Member Functions
ANSI/ISO C++ Professional Programmer's Handbook - 4 - Special Mem nstructor, Copy Constructor, Destructor, And Assignment Operator
file:///D|/Cool Stuff/old/ftp/1/1/ch04/ch04.htm (1 von 24) [12.05.2000 14:46:07]
Trang 10Simulating Inheritance Of Assignment Operator
every class object has four special member functions: default constructor, copy constructor, assignment operator, and
destructor If these members are not explicitly declared by the programmer, the implementation implicitly declares them.This chapter surveys the semantics of the special member functions and their role in class design and implementation.This chapter also examines several techniques and guidelines for effective usage of the special member functions
Constructors
A constructor is used to initialize an object A default constructor is one that can be invoked without any arguments Ifthere is no user-declared constructor for a class, and if the class does not contain const or reference data members, theimplementation implicitly declares a default constructor for it
An implicitly-declared default constructor is an inlinepublic member of its class; it performs the initializationoperations that are needed by the implementation to create an object of this type Note, however, that these operations donot involve initialization of user-declared data members or allocation of memory from the free store For example
indeterminate value after obj has been constructed
ANSI/ISO C++ Professional Programmer's Handbook - 4 - Special Mem nstructor, Copy Constructor, Destructor, And Assignment Operator
file:///D|/Cool Stuff/old/ftp/1/1/ch04/ch04.htm (2 von 24) [12.05.2000 14:46:07]
Trang 11This is because the synthesized default constructor performs only the initialization operations that are required by theimplementation not the programmer to construct an object In this case, C is a polymorphic class An object of thistype contains a pointer to the virtual function table of its class The virtual pointer is initialized by the implicitly-definedconstructor.
Other implementation-required operations that are performed by implicitly-defined constructors are the invocation of abase class constructor and the invocation of the constructor of embedded objects The implementation does not declare aconstructor for a class if the programmer has defined one For example
Calling An Object's Member Function From Its Constructor
Because the virtual pointer is initialized in the constructor before any user-written code, it is safe to call member
functions (both virtual and nonvirtual) of an object from its constructor It is guaranteed that the invoked virtual is theone that is defined in the current object (or of the base class, if it has not been overridden in the current object) However,virtual member functions of objects that are derived from the one whose constructor is executing are not called Forexample
ANSI/ISO C++ Professional Programmer's Handbook - 4 - Special Mem nstructor, Copy Constructor, Destructor, And Assignment Operator
file:///D|/Cool Stuff/old/ftp/1/1/ch04/ch04.htm (3 von 24) [12.05.2000 14:46:07]
Trang 12Please note that if the object's member functions refer to data members of the object, it is the 'programmer's
responsibility to initialize these data members first most preferably with a member-initialization list
(member-initialization lists are discussed next) For example
class Empty {}; //class has no base classes, virtual member functions
//or embedded objects
An implementation can instantiate Empty and Person objects without a constructor In such cases, the
explicitly-declared constructor is said to be trivial, which means that the implementation does not need it in order to
create an instance of its class A constructor is considered trivial when all the following hold true:
Its class has no virtual member functions and no virtual base classes
of size and speed as that which is produced by a C compiler
ANSI/ISO C++ Professional Programmer's Handbook - 4 - Special Mem nstructor, Copy Constructor, Destructor, And Assignment Operator
file:///D|/Cool Stuff/old/ftp/1/1/ch04/ch04.htm (4 von 24) [12.05.2000 14:46:07]
Trang 13Avoid Reduplicating Identical Pieces Of Constructors' Code
It is very common to define a class that has more than one constructor For instance, a string class can define oneconstructor that takes const char * as an argument, another that takes an argument of type size_t to indicate theinitial capacity of the string, and a default constructor
is better to move the recurring code into a single nonpublic member function This function is called by every
constructor The results are shorter compilation time and easier future maintenance:
// the following function is called by every user-defined constructor
void init( size_t cap = DEFAULT_SIZE);
size_t size = strlen (s);
init(size + 1); //make room for null terminating character
length = size;
strcpy(pc, s);
ANSI/ISO C++ Professional Programmer's Handbook - 4 - Special Mem nstructor, Copy Constructor, Destructor, And Assignment Operator
file:///D|/Cool Stuff/old/ftp/1/1/ch04/ch04.htm (5 von 24) [12.05.2000 14:46:07]