Week 5 Lecture Summary for CSC 309
These notes are not intended to be complete. They serve as an outline to the class and as a supplement to the text.
Constructors
- Member functions with the same name as the class.
- A constructor is executed automatically whenever we create an object of the class type.
- Constructors are used to give the object an initial state.
- Constructors have no return type specified.
- Class constructors can be overloaded.
- If you do not define a constructor the compiler will generate a default constructor for you.
- The default constructor or no-arg constructor is a constructor with no parameters.
Example
class Complex { private: double real; double imag; public: void set(double r, double i) { real = r; imag = i; } : : }; int main(){ Complex c1,c2,c3; // okay, default is called Complex nums [30]; // okay, default is called 30 times. }
Note: if you define at least one constructor the compiler will not generate the default constructor.
Example
class Complex { private: double real; double imag; public: Complex (double r, double i) { real = r; imag = i; } : }; int main() { Complex c1,c2,c3; // error default taken away. Complex nums [10]; // error default taken away. }
The default or no-arg constructor is implicitly called in certain situations.
Constructor Inializer List
Complex1(double r, double i) : real(r), imag(i) {}
The Copy Constructor
- A constructor with a single parameter that is a reference to the same class type.
- Its purpose is to create an object that represents a copy of another .
- The copy constructor is implicitly called by the compiler in certain situations.
- You can define your own copy constructor or use the default one generated by the compiler.
- The default copy constructor copies the original object's data members to the target object. Thus the two objects would have exactly the same data members after the copy.
- The copy constructor is also invoked when passing an object to a function by value
- Passing and returning an object by value is inefficient. Usually best to pass by reference.
- You should explicitly define a copy constructor when the class has one or more pointer variables.
- Note: The Complex1 class does not need an explicit copy constructor because it does not have any member variables that are pointers. If you were to define one its signature would look like this
Example
Complex1 c1(2,4); Complex1 c2(c1); // Call to copy constructor Complex1 c3 = c2; // Call to copy constructor
double mod(Complex1 c) { return sqrt( c.getReal() * c.getReal() + c.getImag() * c.getImag()); } int main() { Complex1 number(3,4); mod(number); // copy constructor is called on the parameter object c }
double mod(const Complex1 & c) { return sqrt( c.getReal() * c.getReal() + c.getImag() * c.getImag()); }
Complex1 (const Complex1 & cnumber);
Convert Constructor
- A one parameter constructor used to convert from one type to the class type.
- Complex1 convert constructor.
Complex1(double r) : real(r), imag(0.0) { }
Complex1 c5 = 56.78; // call to convert constructor mod(45.67); // call to convert constructor Complex c4 (3,4); c4 = 67; // call to convert constructor.
The Assignment Operator
- Just like the copy constructor the compiler provides a default assignment operator
=
. - The assignment operator performs similarly to the compiler generated copy constructor.
- Each member of the right hand object is assigned to the corresponding member of the left hand object.
- Like the copy constructor you explicitly overload the assignment operator when the class has one or more pointer variables
- The destructor is a special member function that automatically invoked when the object goes out of scope
- The compiler generates a default destructor.
- You should define your own destructor if your class is dynamically allocating memory.
- Note: The Complex1 class does not need an explicit destructor. The Destructor for the Complex1 class would look like this.
- Two types of method parameters, implicit and explicit.
- When an object calls a member function, the address of that object is assigned to a pointer named this.
this
is of pointer type.- Think of it like this. When we invoke the
setReal
methodc1.setReal(4);
The compiler translates it intoComplex1::setReal(&c1, 4);
The signature of the method would look likeComplex1::setReal(Complex1 *this, double r);
Sincethis
is a pointer that is implicitly defined for us, we could use pointerthis
to access the members of the object within a member function.double Complex1::getReal() const { return real; }
Could be written asdouble Complex1::getReal() const { return (*this).real; }
this
is aconst
pointer.- We can dynamically allocate an Object.
- To access members of the object thought the pointer, we must dereference first
'*'
then use the dot operator'.'
to access the member. - Must be careful the dot operator has higher precedence than dereference operator.
- Can use the indirect selection operator -> instead
- Can return a pointer to an object from a function.
- What is wrong with the following ?
- Anything wrong with this ?
Example
Complex1 c1(4,6), c2; c2 = c1; // Uses default assignment operator.
The Destructor
~Complex1();
The copy constructor, the assignment operator and the destructor are called the Big Three. They should be defined whenever your class has members that are dynamically allocated.
The this
Pointer
Pointers to Objects
Complex1 * c1 = new Complex1(2,3); Complex1 * c2; c2 = new Complex1(3,5);
cout << (*c1).toString(); (*c2).setReal(34);
cout << c1 -> toString(); c2 -> setReal(34);
Complex1 *random() { Complex1 * z = new Complex1; z -> setReal( rand() % 1000 ); z -> setImag( rand() % 1000 ); return z; } int main() { Complex1 * z = random(); cout << z -> toString(); delete z; }
Complex1 *random() { Complex1 z; z.setReal( rand() % 1000 ); z.setImag( rand() % 1000 ); return &z; }
Complex1 random() { Complex1 z; z.setReal( rand() % 1000 ); z.setImag( rand() % 1000 ); return z; }
A Class With Pointer Members
class Name { private: char *first; char *last; public: Name(); Name(char *f, char *l); Name(const Name & n); ~Name(); const Name & operator= (const Name &rhs); void println(ostream& out) const; }; Name::~Name(){ delete [] first; delete [] last; } Name::Name() { first = new char[1]; first[0] = '\0'; last = new char[1]; last[0] = '\0'; } Name::Name(char *f, char *l) { if(NULL == f){ f = "NONE"; } if(NULL == l){ l = "NONE"; } first = new char[strlen(f) + 1]; strcpy(first, f); last = new char[strlen(l) + 1]; strcpy(last,l); } Name::Name(const Name & n) { first = new char[strlen(n.first) + 1]; strcpy(first, n.first); last = new char[strlen(n.last) + 1]; strcpy(last,n.last); } const Name & Name::operator= (const Name& rhs) { if(this != &rhs){ if(strlen(first) != strlen(rhs.first)){ delete [] first; first = new char [strlen(rhs.first) + 1]; } if( strlen(last) != strlen(rhs.last)) { delete [] last; last = new char [strlen(rhs.last) + 1]; } strcpy(first, rhs.first); strcpy(last, rhs.last); } return *this; } void Name::println(ostream & out) const{ out << last << ", " << first << endl; }