Assignment Copy Overload Steps Logic

This chapter is from the book 

Overloading Operators

If you're an OOP programmer, you know that you can also overloadoperators, not just methods. You do that by defining methodsusing the keyword. Being able to overload operators like, , and so on for your own classes and structslets you use those classes and structs with those operators, just as if theywere types built into C#. C# doesn't allow as many operators to beoverloaded as C++ does. You can see the possibilities for C# in Table 3.3. Notethe division into unary operators and binary operators—unaryoperators take one operand (like the negation operator, ), andbinary operators take two operands (like the addition operator,).

Table 3.3 Overloading Possibilities for C# Operators

Operators

Overloading Possibilities

, , , , , ,,

These unary operators can be overloaded.

, , , , , ,|, , ,

These binary operators can be overloaded.

, , , , ,

The comparison operators can be overloaded.

, ||

The conditional logical operators cannot be overloaded, but they are computedwith and |, which can be overloaded.

The array indexing operator cannot be overloaded, but you can define indexersin C# (see Chapter 6, "Understanding Collections and Indexers").

The cast operator cannot be overloaded directly, but you can define your ownconversion operators, as you'll do in this chapter.

, , , , ,, |, , ,

Assignment operators cannot be overloaded, but if you overload a binaryoperator, such as , is also overloaded.

, , , , ,, ,

These operators cannot be overloaded.


Note also that, unlike C++, the assignment operatorcannot be overloaded in C#. An assignment always performs a simple bit-by-bitcopy of a value into a variable. On the other hand, when you overload a binaryoperator like , the corresponding compound assignment operator,, is automatically overloaded. Cast operations are overloaded byproviding conversion methods, as we'll see in a page or two.

For C++ Programmers

Unlike C++, you cannot overload the , , ,, ||, and operators in C#.

You can overload operators for either classes or structs. To see how thisworks, we'll overload the struct we built earlier in thechapter (see Listing 3.6). This struct holds complex numbers like 1 + 2i, wherei is the square root of -1, and we'll see how to overload operators like so that we can add two objects, or the unarynegation operator so that if holds 1 + 2i, will yield -1 - 2i. All this takes place in ch03_14.cs, which appears in Listing3.14. We'll take this code apart in the next few sections.

Listing 3.14 Overloading Operators (ch03_14.cs)

class ch03_14{ public static void Main() { Complex complex1 = new Complex(1, 2); Complex complex2 = new Complex(3, 4); System.Console.WriteLine("complex1 = {0}", complex1); System.Console.WriteLine("complex2 = {0}", complex2); Complex complex3 = -complex1; System.Console.WriteLine("-complex1 = {0}", complex3); System.Console.WriteLine("complex1 + complex2 = {0}", complex1 + complex2); if(complex1 == complex2){ System.Console.WriteLine("complex1 equals complex2"); } else { System.Console.WriteLine("complex1 does not equal complex2"); } }}public struct Complex { public int real; public int imaginary; public Complex(int real, int imaginary) { this.real = real; this.imaginary = imaginary; } public override string ToString() { if (imaginary >= 0){ return(System.String.Format("{0} + {1}i", real, imaginary)); } else { return(System.String.Format("{0} - {1}i", real, System.Math.Abs(imaginary))); } } public static Complex operator-(Complex complex) { return new Complex(-complex.real, -complex.imaginary); } public static Complex operator+(Complex complex1, Complex complex2) { return new Complex(complex1.real + complex2.real, complex1.imaginary + complex2.imaginary); } public static implicit operator Complex(int theInt) { return new Complex(theInt, 0); } public static explicit operator int(Complex complex) { return complex.real; } public static bool operator==(Complex complex1, Complex complex2) { if (complex1.real == complex2.real && complex1.imaginary == complex2.imaginary) { return true; } return false; } public static bool operator!=(Complex complex1, Complex complex2) { return !(complex1 == complex2); } public override bool Equals(object obj) { if (!(obj is Complex)) { return false; } return this == (Complex) obj; } public override int GetHashCode() { return (int) System.Math.Sqrt(real * real + imaginary * imaginary); }}

Creating the Complex Struct

We start ch03_14.cs by using the struct we saw earlier inthis chapter, which has a constructor you pass the real and imaginary parts to,and we'll add the method. Any time C# needs a stringrepresentation of a complex number (as when you pass it to), it'll call the number's method:

public struct Complex { public int real; public int imaginary; public Complex(int real, int imaginary) { this.real = real; this.imaginary = imaginary; } public override string ToString() { if (imaginary >= 0){ return(System.String.Format("{0} + {1}i", real, imaginary)); } else { return(System.String.Format("{0} - {1}i", real, System.Math.Abs(imaginary))); } } . . .}

Overloading a Unary Operator

The next step is to start overloading operators for numbers.We'll start by overloading the unary negation operator, . To dothat, you add this method to the struct, which uses the keyword and is passed a number to negate:

public static Complex operator-(Complex complex) { return new Complex(-complex.real, -complex.imaginary);}

Using System.String.Format

Note the use of in the previous code. Thismethod works just like , except that itreturns a string instead of displaying text to the console. That means we canuse it to embed the real and imaginary parts of the complex number into astring.

All we have to do here is to negate the real and imaginary parts of thecomplex number and return the result, as you see in this code. Now if youcreated a complex number, 1 + 2i, and negated it like this:

Complex complex1 = new Complex(1, 2);System.Console.WriteLine(-complex1;

You'd see this result:

-1 - 2i

Overloading a Binary Operator

We've been able to overload the unary operator for complexnumbers by adding a static method to the struct that uses the keyword and is passed the operand to negate. When you'reoverloading a binary operator, like the addition operator, you arepassed two operands; in the case of the operator, those are thecomplex numbers you're supposed to add. Here's the method you'dadd to the struct to overload the operator forcomplex numbers:

public static Complex operator+(Complex complex1, Complex complex2) { return new Complex(complex1.real + complex2.real, complex1.imaginary + complex2.imaginary);}

Now if you were to use this code to add 1 + 2i and 3 + 4i:

Complex complex1 = new Complex(1, 2);Complex complex2 = new Complex(3, 4);System.Console.WriteLine("complex1 + complex2 = {0}", complex1 + complex2);

you would see this result:

complex1 + complex2 = 4 + 6i

Overloading Conversion Operations

You can also overload conversion operations. Conversions can be eitherimplicit or explicit, and you use the or keywords in those cases. The name of the operator in this case is the targettype you're converting to, and the parameter you're passed is of thetype you're converting from. For example, here's how to convert froman integer value to a number—note that we'll justassign the integer value to the real part of the resulting complex number.Because data will be lost, we'll make this an implicit conversion:

public static implicit operator Complex(int intValue){ return new Complex(intValue, 0);}

On the other hand, converting from a complex number to an doesimply some data loss, so we'll make this an explicit conversion:

public static explicit operator int(Complex complex){ return complex.real;}

Now when you cast from to explicitly, thismethod will be called.

Overloading Equality Operators

Overloading the equality operator is like overloading any binaryoperator, with a few differences. For one, if you overload , C# willinsist that you overload as well, so we'll do both operatorshere.

For C++ Programmers

If you overload , C# will insist that you also overload.

When you overload the operator, you're passed two objects to compare; you return if they'reequal and otherwise. numbers are equal if theirreal and imaginary parts are equal, so here's how to overload the operator for complex numbers:

public static bool operator==(Complex complex1, Complex complex2){ if (complex1.real == complex2.real && complex1.imaginary == complex2.imaginary) { return true; } return false;}

And here's how to overload :

public static bool operator!=(Complex complex1, Complex complex2){ return !(complex1 == complex2);}

If you only overload and , C# will give you a warningwhen you compile your code that you haven't overridden the method. This method is sometimes used by codeinstead of the operator to check for equality (in Visual Basic .NET,for example, you can't overload operators, so code would use the method). For example, to check if equals, you could call . C# wantsus to override this method, replacing the default version in the class, not overload it, and we'll discuss overridingmethods in the next chapter. All that means in this case is that we use the keyword here. After using the operator to ensurethat the object passed to us is a object, we just compare thecurrent object to the one passed, and return the result of that comparison, likethis:

public override bool Equals(object obj){ if (!(obj is Complex)) { return false; } return this == (Complex) obj;}

But there's still more to do. If you've overloaded,, and , C# will still give you anotherwarning. You haven't overridden the method. Ahash method is used to quickly generate a hash code, which is an that corresponds to the value of an object. Hash codes allow C# to store objectsmore efficiently in collections, as we'll discuss in Chapter 6. Youdon't have to override —you can simply ignore thewarning. In this case, we'll return the magnitude of the complex number asits hash code:

public override int GetHashCode() { return (int) System.Math.Sqrt(real * real + imaginary * imaginary);}

Now, at last, you can compare two complex numbers using the operator, like this, which compares 1 + 2i and 3 + 4i:

Complex complex1 = new Complex(1, 2);Complex complex2 = new Complex(3, 4);if(complex1 == complex2){ System.Console.WriteLine("complex1 equals complex2");} else { System.Console.WriteLine("complex1 does not equal complex2");}

Here's what this code produces:

complex1 does not equal complex2

For the full story on operator overloading, run ch03_14.cs; this exampleimplements all the operator overloads we've discussed and puts them to workusing the code we've developed. Here's what you see when you run thisexample:

C:\>ch03_14complex1 = 1 + 2icomplex2 = 3 + 4i-complex1 = -1 - 2icomplex1 + complex2 = 4 + 6icomplex1 does not equal complex2

Copy-and-swap[edit]

Intent[edit]

To create an exception safe implementation of overloaded assignment operator.

Also Known As[edit]

Create-Temporary-and-Swap

Motivation[edit]

Exception safety is a very important corner stone of highly reliable C++ software that uses exceptions to indicate "exceptional" conditions. There are at least 3 types of exception safety levels: basic, strong, and no-throw. Basic exception safety should be offered always as it is usually cheap to implement. Guaranteeing strong exception safety may not be possible in all the cases. The copy-and-swap idiom allows an assignment operator to be implemented elegantly with strong exception safety.

Solution and Sample Code[edit]

Create a temporary and swap idiom acquires new resource before it forfeits its current resource. To acquire the new resource, it uses RAII idiom. If the acquisition of the new resource is successful, it exchanges the resources using the non-throwing swap idiom. Finally, the old resource is released as a side effect of using RAII in the first step.

classString{char*str;public:String&operator=(constString&s){Stringtemp(s);// Copy-constructor -- RAIItemp.swap(*this);// Non-throwing swapreturn*this;}// Old resources released when destructor of temp is called.voidswap(String&s)throw()// Also see the non-throwing swap idiom{std::swap(this->str,s.str);}};

Some variations of the above implementation are also possible. A check for self assignment is not strictly necessary but can give some performance improvements in (rarely occurring) self-assignment cases.

classString{char*str;public:String&operator=(constString&s){if(this!=&s){String(s).swap(*this);//Copy-constructor and non-throwing swap}// Old resources are released with the destruction of the temporary abovereturn*this;}voidswap(String&s)throw()// Also see non-throwing swap idiom{std::swap(this->str,s.str);}};

copy elision and copy-and-swap idiom

Strictly speaking, explicit creation of a temporary inside the assignment operator is not necessary. The parameter (right hand side) of the assignment operator can be passed-by-value to the function. The parameter itself serves as a temporary.

String&operator=(Strings)// the pass-by-value parameter serves as a temporary{s.swap(*this);// Non-throwing swapreturn*this;}// Old resources released when destructor of s is called.

This is not just a matter of convenience but in fact an optimization. If the parameter (s) binds to an lvalue (another non-const object), a copy of the object is made automatically while creating the parameter (s). However, when s binds to an rvalue (temporary object, literal), the copy is typically elided, which saves a call to a copy constructor and a destructor. In the earlier version of the assignment operator where the parameter is accepted as const reference, copy elision does not happen when the reference binds to an rvalue. This results in an additional object being created and destroyed.

In C++11, such an assignment operator is known as a unifying assignment operator because it eliminates the need to write two different assignment operators: copy-assignment and move-assignment. As long as a class has a move-constructor, a C++11 compiler will always use it to optimize creation of a copy from another temporary (rvalue). Copy-elision is a comparable optimization in non-C++11 compilers to achieve the same effect.

StringcreateString();// a function that returns a String object.Strings;s=createString();// right hand side is a rvalue. Pass-by-value style assignment operator // could be more efficient than pass-by-const-reference style assignment // operator.

Not every class benefits from this style of assignment operator. Consider a String assignment operator, which releases old memory and allocates new memory only if the existing memory is insufficient to hold a copy of the right hand side String object. To implement this optimization, one would have to write a custom assignment operator. Since a new String copy would nullify the memory allocation optimization, this custom assignment operator would have to avoid copying its argument to any temporary Strings, and in particular would need to accept its parameter by const reference.

Known Uses[edit]

Related Idioms[edit]

References[edit]

0 comments

Leave a Reply

Your email address will not be published. Required fields are marked *