Note 3 - University of Wisconsin

advertisement
Computer Science and Software Engineering
University of Wisconsin - Platteville
3. Big3
Yan Shi
CS/SE 2630 Lecture Notes
The Big Three
In C++, classes come with 3 special functions that are
already written for you, known collectively as the big
three. In many cases, you can accept the default behavior
of these function.
By default:
 Destructor: calls the destructors of all
members/release all primitive members.
 Copy Constructor: applies copy
constructors/assignments to each data member
 operator=: applies assignment operator to each data
member
Example: default
class Cell
{
private:
int value;
public:
int Get()
{
return value;
}
void Set( int x )
{
value = x;
}
};
Default Big3!
void main()
{
Cell a;
a.Set(2);
Cell b = a;
Cell c(a);
b.Set(8);
c = b;
a.Set(5);
cout << a.Get() << " "
<< b.Get() << " "
<< c.Get() << endl;
}
Example: Default Big3
Cell( const Cell & c )
{
value = c.value;
}
~Cell(){ }
Cell& operator=( const Cell &rhs )
{
if ( this != & rhs ) // must check for self assignment!
value = rhs.value;
return *this; // return a reference to myself
}
this is a pointer to the object that the member function is being called on.
a = b is the same as a.operator=(b)
Pointers and the Big Three
 If a class contains pointers, we may have problem
with defaults!!
— we must delete pointers by ourselves.
— The copy constructor and operator= only do “shallow
copying”: giving us two class instances with a pointer
to the same object.

we expect “deep copying”
  We need to implement the Big Three by
ourselves!
Example: With Pointer Member
class Cell
{
private:
int *value;
public:
int Get()
{
return *value;
}
void Set( int x )
{
*value = x;
}
};
Is this code correct?
void main()
{
Cell a;
a.Set(2);
Cell b = a;
Cell c(a);
b.Set(8);
c = b;
a.Set(5);
cout << a.Get() << " "
<< b.Get() << " "
<< c.Get() << endl;
}
No! value is not initialized!
Example: With Pointer Member
class Cell
{
private:
int *value;
public:
Cell ( int x = 0 )
{
value = new int(x);
}
int Get()
{
return *value;
}
void Set( int x )
{
*value = x;
}
};
void main()
{
Cell a;
a.Set(2);
Cell b = a;
Cell c(a);
b.Set(8);
c = b;
a.Set(5);
cout << a.Get() << " "
<< b.Get() << " "
<< c.Get() << endl;
}
Function overloading:
If there is no parameter x,
assume x = 0.
Example: With Pointer Member
class Cell
{
private:
int *value;
public:
Cell ( int x = 0 )
{
value =
new int(x);
}
int Get()
{
return *value;
}
void Set( int x )
{
*value = x;
}
};
void main()
{
Cell a;
a.Set(2);
Cell b = a;
Cell c(a);
b.Set(8);
c = b;
}
// What is b’s value?
a.Set(5); // Will b and c’s values change?
cout << a.Get() << " "
<< b.Get() << " "
<< c.Get() << endl;
// Is there any memory leak?
Example: Implement Big3
Cell( const Cell & c )
{
value = new int( *c.value );
}
~Cell(){ delete value; }
Cell& operator=( const Cell &rhs )
{
if ( this != & rhs ) // must check for self assignment!
*value = *rhs.value;
return *this; // return a reference to myself
}
Destructor
 Destructor is called when an object goes out of
scope.
 A class can have only one destructor without any
parameters.
 When there are pointer data members in a class,
you should implement your own destructor to
— delete all dynamically allocated memory space
— other operations as needed
Operator Overloading
 In C++, you can give special meanings to
operators when they are used with user-defined
classes.
•
•
•
•
= (assignment operator)
+ - * (binary arithmetic operators)
+= -= *= (compound assignment operators)
== != > < >= <= (comparison operators)
 Implement operator overloads by providing
special member-functions in your classes.
Assignment Operator=
class MyClass
{
public:
...
// the = operator don’t change the rhs, only the lhs
// return a reference to allow operator chaining
// why not returning const MyClass&?
//
to enable operations such as ( a = b ) = c
// "If it's good enough for ints,
// it's good enough for user-defined data-types."
MyClass & operator=(const MyClass &rhs);
...
}
MyClass a, b, c, d;
...
b = a; // Same as b.operator=(a);
d = c = b = a; // assignment is right-associative.
// same as d = ( c = ( b = a)));
Assignment Operator=
The typical sequence of operations:
MyClass & operator=(const MyClass &rhs)
{
//0. Self assignment check!
//1. Deallocate any memory that MyClass is using internally
//2. Allocate some memory to hold the contents of rhs
//3. Copy the values from rhs into this instance
//4. return *this;
}
the member function needs to return a reference to the object.
So, it returns *this, which returns what this points at (i.e. the object).
(In C++, instances are turned into references, and vice versa,
because references are treated as alternative names for instances,
so C++ implicitly converts *this into a reference to the current instance.)
Assignment Operator=
You must check for self assignment!!!
What happens if we do:
MyClass obj;
…
obj = obj;
obj will first release any memory it holds internally.
 This completely messes up the rest of the assignment
operator's internals.
Assignment Operator =
Self assignment checking:
MyClass & operator=(const MyClass &rhs)
{
// Check for self-assignment!
if (this != &rhs)
// Same object?
{
... // Deallocate, allocate new space, copy values...
}
return *this;
}
Compound Assignment
Operators += -= *=
// similar prototype as operator=
MyClass & MyClass::operator+=(const MyClass &rhs)
{
... // Do the compound assignment work.
return *this;
}
MyClass a, b;
...
b += a; // Same as b.operator+=(a);
Compound assignment operators are destructive operators:
they update or replace the values on lhs of the assignment.
In general, beware of self-assignment as well.
Binary Arithmetic Operators + - *
 Binary arithmetic operators don’t modify either operand, but
actually return a new value from the two arguments
// pass a const reference, return a const MyClass value
const MyClass MyClass::operator+(const MyClass &other) const
{
MyClass result = *this;
result += other;
//return MyClass(*this) += other;
return result;
}
MyClass a, b, c;
...
c = a + b; // Same as c = a.operator+(b);
 Use the compound operator (+=) implementation to implement
binary (+) operator!
Comparison Operator == and !=
 == and != return true or false
 usually implement == first, then use == to implement !=
// pass a const reference, return a bool value
bool MyClass::operator==(const MyClass &other) const
{
// compare the values and return a bool result }
bool MyClass::operator!=(const MyClass &other) const
{
return !(*this == other); }
MyClass a, b;
...
if ( a == b )// Same as if ( a.operator==(b) )
...
Overloading << and >>
// return a reference for ostream to allow recursive <<
// use friend keyword to access the private members of MyClass
friend ostream& operator<<(ostream& out, const MyClass & m)
{
out << ...what to print m...;
return out;
}
friend istream& operator>>(istream& in, MyClass & m)
{
// whatever code to read m: use in instead of cin
return in;
}
MyClass a,b;
cin >> a >> b;
cout << a << b;
Friend Functions
 A friend function is a function that is not a
member of a class but has access to the class's
private and protected members.
— friend functions are not considered as class
members; they are not in the class's scope, and
they are not called using the dot notation.
— A friend function is declared by the class that is
granting access.
— The friend declaration can be placed anywhere in
the class declaration. It is not affected by the
access control keywords.
Operator overloading
restrictions
 Cannot overload
— scope resolution operator ::
— member access operator ., .*
— ternary conditional operator ?:
 Cannot create new operators
After class exercise:
 How to implement a growable array?
— Hint: define a growable array class; overload
operator[]
 How to overload operator++?
Download