2. Learning Objectives:
• To understand object behaviors
• To understand constructors
• To understand destructors and deep copy
• To understand update methods and query
methods
• To understand const attributes, static attributes,
static methods, and const methods
• To understand template
TCP1201 OOPDS
3. Revisit Pointers
• Why pointer is important?
1. Reference/Point to existing data without cloning
the data.
2. Dynamic memory allocation (create the amount
of data based on runtime need).
3. Dynamic polymorphism (Lecture 4)
• A pointer always stores the address of a data.
• Before we can use a pointer, it must point to a
valid address that is achieved through:
– Pointing it to an existing data, or
– Using it to create a new data (use new operator).
TCP1201 OOPDS
4. Dangling Pointer
• Is pointer that does not point to an valid
address.
• Use of dangling pointer generates runtime
error easily.
int* p; // 'p' usually does not point to
// a valid address.
*p = 10; // Runtime error usually.
?
p [ int* ]
TCP1201 OOPDS
5. Point to Existing Data Without
Cloning the Data
1432
3 a [ int ]
int a = 3;
int *p = &a; 4324
1432 p [ int* ]
cout << *p; // 3
cout << p; // 1432
'a' stores an int . 'p' stores
The ampersand '&' is the address of variable 'a'.
called address operator
which returns the We say 'p' points to 'a'.
address of variable 'a'. 'p' is not a clone of 'a'.
TCP1201 OOPDS
6. Dynamic Memory Allocation
(DMA)
• To create new data dynamically.
• We use new operator to create new data, and
later use delete operator to release the
memory used by the data.
• The new operator allocates the memory
needed for the new data, and returns the
address of the new data.
int a = 3; // Automatic memory allocation.
int* p; // 'p' does not point to a valid address.
p = new int; // 'p' points to the new data.
...
delete p; // Release the memory used by *p.
TCP1201 OOPDS
7. Dynamic Memory Allocation
(DMA) 1432
?
• Use delete [] to release the 1432
p1 [ int* ] [int]
memory used by a dynamic array.
1100 1100
? [int]
p2 [int* ]
1104
? [int]
int* p1 = new int; // Create 1 int. 1108
delete p1; // Release 1 int. ? [int]
1112
? [int]
int* p2 = new int[4]; // Create a dynamic
// array of 4 int.
delete [] p2; // Release dynamic array.
TCP1201 OOPDS
8. 4 Types of Object Behaviors
1. Constructors – Specify how a new object is
created.
2. Queries – Return or find out, but not modify,
the value of attributes of an object, e.g. get
methods.
3. Updates – Modify the value of attributes of
an object, e.g. set methods.
4. Destructors – Specify how an object is
destroyed.
TCP1201 OOPDS
9. Constructors
• Constructors are special types of methods that are
used to initialize object attributes.
• They are invoked/called automatically whenever an
instance/object of a class is created.
class Student {
...
};
int main() {
Student s1; // Create an instance, call a constructor.
Student *ps = new Student; // Create an instance, call a constructor.
...
delete ps; // Release the memory used by *ps.
}
TCP1201 OOPDS
10. Constructors
• Always have the same name as the class name.
• Have no return value.
• 3 types of constructors: default, overloaded, copy.
• A class can have more than one constructor.
class Student {
int id;
string name;
public:
Student(); // Default constructor
Student(int id); // Overloaded constructor
Student(string name); // Overloaded constructor
Student(int id, string name); // Overloaded constructor
Student(const Student& existingStudent); // Copy constructor
...
}; Constructor name is the same as the class name
TCP1201 OOPDS
11. Default Constructors
• Enable us to initialize attributes to preferred default values
when no argument is not provided during object creation.
• A class can have only one default constructor.
class Student {
int id;
string name;
public:
Student() { // Default constructor has no parameter.
name = "Unknown"; // Set default name value to "Unknown".
id = 0; // Set default id value to 0.
}
};
int main() {
Student s1; // Call default constructor, id=0, name="Unknown".
Student *ps = new Student; // Call default constructor, id=0, name="Unknown".
...
delete ps;
...
TCP1201 OOPDS
12. Without Default Constructors
• If you do not provide both default constructor and
overloaded constructor, C++ automatically creates a default
constructor with blank implementation.
// Your code
class Student { // No default constructor or
// overloaded constructor is provided.
int id;
string name;
};
// Compiler code
class Student {
int id;
string name;
public:
Student() // Compiler's default constructor.
{ } // Blank implementation. name = "", id = ?
};
int main() {
Student s1; // Call default constrcutor. name = "", id = ?
Student *ps = new Student; // name = "", id = ?
...
TCP1201 OOPDS
13. Constructor Initializer List
• Enable us to initialize attributes before constructor body is
executed.
• Is placed between the constructor parameter list and
constructor body.
• General format:
ClasName (<parameter list>) : <initializer list> { ... }
Constructor name Colon Constructor body
Initializer list
ClassName (datatype1 param1, datatype2 param2)
: attribute1 (param1), attribute2 (param2)
{ ... }
TCP1201 OOPDS
14. Constructor Initializer List
• The previous Student's default constructor can be
rewritten to use constructor initializer list as follow:
// Use constructor initializer list.
class Student {
int id;
string name;
public:
Student () // Default constructor
: name("Unknown"), id(0) // Initialize name = "Unknown", id = 0.
{ }
};
int main() {
Student s1; // name = "Unknown", id = 0.
Student *ps = new Student; // name = "Unknown", id = 0.
...
TCP1201 OOPDS
15. Constructor Initializer List
• Is the only way to initialize const attributes (discuss
later), and is also only way to call superclass'
constructor (Lecture 3).
• Hence is the preferred way to initialize attributes.
• Does not work on static attributes (discuss later).
TCP1201 OOPDS
16. Example: Default Constructor
class Student { Output:
int id;
string name; A Student object is created!
public: Student id = 0
Student() // Default constructor Student name = "Unknown"
: id(0), name("Unknown") { Student id = 123
cout << "A Student object is created!n"; Student name = "Ali"
}
void setName (string name) { this->name = name; }
void setID (int id) { this->id = id; }
string getName() { return name; }
int getID() { return id; }
};
int main() {
Student* s1 = new Student; // Call default constructor,
// id = 0, name = "Unknown"
cout << "Student id = " << s1->getID() << endl
<< "Student name = " << s1->getName() << endl;
s1->setID (123); // id = 123
s1->setName ("Ali"); // name = "Ali"
cout << "Student id = " << s1->getID() << endl
<< "Student name = " << s1->getName() << endl;
delete s1;
}
TCP1201 OOPDS
17. Overloaded Constructors
• Constructors that have parameter(s).
• Initialize attributes to values passed as arguments.
class Student {
int id;
string name;
public:
Student (int id, string name) // Overloaded constructor.
: id(id), name(name) // attribute1(param1), attribute2(param2).
{ }
};
int main() {
Student s1 (123, "Ali"); // Call overloaded constructor, id = 123,
// name = "Ali"
Student *ps;
ps = new Student (234, "Bob"); // Call overloaded constructor, id = 234,
// name = "Bob"
...
TCP1201 OOPDS
18. Overloaded Constructors
• A class can have more than one overloaded constructors
as long as the parameter lists follow the rules of function
overloading, that is no 2 overloaded constructors should
have the same number of parameters and the same type
of parameters.
class Student {
int id;
string name;
public:
Student () {...} // Default constructor
Student (int id) {...} // Overloaded constructor 1
Student (string name) {...} // Overloaded constructor 2
Student (int id, string name) {...} // Overloaded constructor 3
Student (const Student& s) {...} // Copy constructor
};
int main() {
Student s1; // Call default constructor
Student s2 ("Ann"); // Call overloaded constructor 2
Student s3 (234, "Bob"); // Call overloaded constructor 3
Student s4 (123); // Call overloaded constructor 1
Student s5 (s4); // Call copy constructor
... TCP1201 OOPDS
19. Overloaded Constructors
• We may provide default argument to constructor
parameters.
• If all parameters of an overloaded constructor have default
argument, the overloaded constructor serves as a default
constructor too.
class Student {
string name;
int id;
public:
Student (int id=0, string name="Unknown") // 2-in-1: default constructor +
// overloaded constructor.
: id(id), name(name) {}
};
int main() {
Student *p1 = new Student; // Call default constructor,
// id = 0, name = "Unknown".
Student *p2 = new Student (234, "Bob"); // Call overloaded constructor,
// id = 234, name = "Bob".
...
TCP1201 OOPDS
20. Copy Constructors
• Initializes a new object by copying the value of attributes
from an existing object of the same class.
• Is invoked when an instance is created with one of the
following ways:
Student x(123, "Michael"); // x invokes overloaded constructor.
Student y = x; // y invokes copy constructor, copy from x.
Student z(y); // z invokes copy constructor, copy from y.
Student* s = new Student(y); // *s invokes copy constructor,
// copy from y.
// All 4 objects x, y, z and *s have id=123 and name="Michael".
TCP1201 OOPDS
21. Copy Constructors
• A class can have one copy constructor only.
• Copy constructor must have the following
header/interface/signature:
Parameter name refers to the source instance that is being “copied”
Parameter is of the same type as the class
ClassName (const ClassName& existingInstance);
Pass by reference to avoid cloning the parameter
const prevents existing instance from being modified by the copy constructor
Student (const Student& existingStudent);
TCP1201 OOPDS
22. Copy Constructors
• If we do not provide a copy constructor, C++
automatically provides a default copy constructor that
copies the value of all attributes from current instance to
the new instance. This type of copying is called shallow
copy.
• For our Student class, C++ automatically provides the
following default copy constructor.
class Student {
int id; // 2 attributes, no dynamic memory allocation.
string name;
...
// C++ provides the following default copy constructor.
Student (const Student& s)
: id(s.id), name(s.name) // shallow copy 2 attributes.
{ }
};
TCP1201 OOPDS
23. Deep Copy
• We usually do not have to provide a copy
constructor, unless we want to perform additional
initialization that is not provided by the default copy
constructor. For example, deep copy a pointer attribute
that is created using dynamic memory allocation (DMA).
• If a pointer attribute of a source object uses DMA to
create its data, shallow copy won't create that data in the
target object but just makes the source object shares the
data with target object.
TCP1201 OOPDS
24. Deep Copy Example
• Consider a scenario whereby a student have many
marks.
• We declare a pointer named marks in Student class, and
use it to create a dynamic array to store the marks.
• Each student should have their own dynamic array to
store their marks, and they should not share the same
dynamic array.
• Shallow copying the marks attribute would result in both
the source student and target student sharing the same
dynamic array. Which is considered wrong.
• Deep copying the marks attribute would ensure that
each student would have their own dynamic array for
storing the marks.
TCP1201 OOPDS
25. Problem: Shallow copy a pointer attribute in
Copy Constructor
class Student { int main() {
int size; Student *s1, *s2;
double *marks; // Pointer attribute. s1 = new Student (3);
public: s2 = new Student (*s1);
Student (int size); // Overloaded constructor. s1->print(); // 0 0 0
// No copy constructor is written, C++ provides one. s2->print(); // 0 0 0
void setMark (int index, int mark); s1->setMark (0, 70);
void print(); s1->setMark (1, 80);
}; s1->setMark (2, 90);
Student::Student (int size) s1->print(); // 70 80 90
: size(size) { s2->print(); // 70 80 90
this->marks = new double[size]; // Create dynamic array. delete s1;
for (int i = 0; i < size; i++) delete s2;
marks[i] = 0; }
}
void Student::setMark (int index, int mark) {
marks[index] = mark; Output:
} Marks = 0 0 0
void Student::print() { Marks = 0 0 0
cout << "Marks = ";
for (int i = 0; i < size; i++) Marks = 70 80 90
cout << marks[i] << " "; Marks = 70 80 90
cout << endl;
}
s2 points to s1's marks
array, hence sharing one marks
array
TCP1201 OOPDS
26. Solution: Deep copy in Copy Constructor
class Student { int main() {
int size; Student *s1, *s2;
double *marks; // Pointer attribute. s1 = new Student (3);
public: s2 = new Student (*s1);
Student (int size); // Overloaded constructor. s1->print(); // 0 0 0
Student (const Student& s); // Copy constructor. s2->print(); // 0 0 0
void setMark (int index, int mark); s1->setMark (0, 70);
void print(); s1->setMark (1, 80);
}; s1->setMark (2, 90);
Student::Student (int size) s1->print(); // 70 80 90
: size(size) { s2->print(); // 0 0 90
this->marks = new double[size]; // Create dynamic array. delete s1;
for (int i = 0; i < size; i++) delete s2;
marks[i] = 0; }
}
Student::Student (const Student& s) // Copy constructor.
: size(s.size) { Output:
this->marks = new double[size]; // Create dynamic array. Marks = 0 0 0
for (int i = 0; i < size; i++) Marks = 0 0 0
marks[i] = s.marks[i]; // Copy array.
} Marks = 70 80 90
void Student::setMark (int index, int mark) { Marks = 0 0 0
marks[index] = mark;
}
void Student::print() {
cout << "Marks = "; s1 and s2 each have a
for (int i = 0; i < size; i++)
cout << marks[i] << " "; different marks array
cout << endl;
}
TCP1201 OOPDS
27. Destructors
• A destructor is a special type of method that is invoked
whenever an instance is destroyed (de-allocating an
instance).
• Destructors are automatically called when an
automatically allocated instance goes out of scope, or
when a dynamically allocated instance is explicitly
deleted (using delete operator).
• Destructors have no return value and no parameter
• There is only one destructor for each class.
• Destructor has the same name as the class, except
that it is preceded by a tilde „~‟. class Student {
...
~Student();
};
TCP1201 OOPDS
28. Destructors
• Destructors are generally used to perform any cleanup
necessary prior to an instance being destroyed. For
example deleting dynamically allocated objects:
// Continue from Deep Copy example
class Student {
double *marks; // Pointer attribute.
public:
...
~Student();
};
Student::~Student() {
delete [] marks; // Destroy dynamic attribute.
}
TCP1201 OOPDS
29. Destructors
• Same as the default constructor and copy constructor
, if you do not provide a destructor, C++ provides a
default destructor with blank implementation.
// Your code
class Student { // No destructor is provided.
};
// Compiler code
class Student {
public:
-Student() { // Compiler's default destructor,
// blank implementation.
}
};
TCP1201 OOPDS
30. Example: Destructor
class Student {
int id; Output:
public:
Student(int id); // constructor Object 000 created.
~Student(); // destructor Object 000 destroyed.
}; Object 111 created.
Student::Student(int id) Object 222 created.
: id(id) { Object 333 created.
cout << "Object " << id << " created.n"; Object 222 destroyed.
} Object 111 destroyed.
Student::~Student() {
cout << "Object " << id << " destroyed.n";
}
int main() {
Student* s0 = new Student(000); // constructor for 000
delete s0; // destructor for 000
Student s1(111); // constructor for 111
Student s2(222); // constructor for 222
Student* s3 = new Student(333); // constructor for 333
} // End of main() auto-calls destructor for 222 and 111.
// Why destructor for 333 not called? No "delete s3;"
TCP1201 OOPDS
31. Query Methods
• Query methods (also called accessors) are methods
that are used to inquire (find out) about the value of an
instance‟s attributes.
• Query method does not modify the value of any of the
object‟s attributes.
class Student {
int id;
string name;
...
int getId() { // Returns the value of attribute id.
return id;
}
string getName() { // Returns the value of attribute name.
return name;
}
};
TCP1201 OOPDS
32. Update Methods
• Update methods (also called mutators) are methods
that modify the value of attribute(s) in an object.
class Student {
int id;
string name;
...
void setId (int id) { // Modify attribute 'id'.
this->id = id;
}
void setName (string name) { // Modify attribute 'name'.
this->name = name;
}
};
TCP1201 OOPDS
33. Update Methods
• Recall that one of the reasons to have encapsulation is to
ensure data integrity.
• Update methods allow us to achieve data integrity.
• In the following example, the setGPA update method
ensures that the new GPA value is within 0-4.
int Student::setGPA (double newGPA) {
if ((newGPA >= 0.0) && (newGPA <= 4.0)) {
gpa = newGPA;
return 0; // Return 0 to indicate success
}
else {
return -1; // Return -1 to indicate failure
}
}
TCP1201 OOPDS
34. Update & Query Behaviors
• Identify the type of behaviors of
the Student class on the right.
• Query behaviors:
Student
– getId, show_subjects.
-id: int
• Update behaviors: -subjects:string[*]
+getId():int
– register_subject (modify attribute +setId(id:int):void
subjects). +show_subjects():void
– withdraw_subject (modify attribute +register_subject():void
+withdraw_subject():void
subjects).
TCP1201 OOPDS
35. const Attributes
• const attributes are attributes that its value cannot be
changed throughout the lifetime of the instance.
• It must be initialized using constructor initializer list.
class Student {
const int id; // Use keyword "const" to declare
const string name; // const attribute.
public:
Student (int id, string name) // Overloaded constructor.
: id(id), name(name) // Constructor initializer list.
{}
};
int main() {
Student* s1 = new Student(123, "Michael");
// Cannot change s1's id and name after creation.
...
}
TCP1201 OOPDS
36. static Attributes
• Are attributes that are shared across all instances of a
class.
• The program creates only a single instance of a static
attribute, regardless of how many instances of the
class are created.
• Cannot be initialized within the class or using
constructor initializer list.
• Must be initialized outside the class declaration using
scope resolution operator "::".
class Student {
string name;
static int count; // Use keyword "static" to declare
// static attribute.
}; // end of class
int Student::count = 0; // Initialize count to 0.
TCP1201 OOPDS
37. static Methods
• A static method can be invoked without creating an
instance of the class.
• Use the scope resolution operator "::".
• They cannot access non-static attributes or non-static
methods.
• The primary reason to declare static methods is to be
able to access static attributes without creating an
instance of the class.
TCP1201 OOPDS
39. static Members
int main() {
cout << Student::getCount(); // Invokes static method
// without instance.
Student* s1 = new Student; // 1 instance.
cout << s1->getCount(); // Invokes static method
// via the instance s1.
vector<Student> s; // 100 instances.
cout << s[0].getCount(); // Invokes static method
// via the instance s[0].
// At this point, there are 101 instances of name,
// but only 1 instance of count.
delete s1;
}
TCP1201 OOPDS
40. const and static Attributes
• To summarize:
– Attributes that you want to “share” across all
instances of a class should be declared as static.
– Attributes that you want to prevent modification
after the creation of the instance should be
declared as const.
TCP1201 OOPDS
41. const Methods
• Query methods are typically declared as const to indicate
that the methods should not modify the value of any
attributes.
• A compile-error will be generated if a const method tries to:
– Modify the value of an attribute.
– Call to non-const or non-static method.
class Student {
int gpa;
public:
void calc() { ... } // Non-const method.
void try() const { // const method.
gpa = 4.0; // Compile-error, try to modify attribute.
calc(); // Compile-error, call non-const/non-static method.
}
int getGPA() const { // const method, fine, no code modifies attribute.
return gpa;
}
};
TCP1201 OOPDS
42. Template
• Template allows us to write generic functions
or classes (called function template, class
template) that accept any data type as
parameters or attributes.
• During compilation, the compiler will produce a
separate definition for every data type that
uses the template.
TCP1201 OOPDS
43. Function Template
• To swap 2 integers, we may write the following
function: void Swap (int& m, int& n) {
int temp = m;
m = n;
n = temp;
}
• To swap 2 strings, we need to write another
function: void Swap (string& s1, string& s2) {
string temp = s1;
s1 = s2;
s2 = temp;
}
The only different between these 2 functions are
data type. Template is the best solution.
TCP1201 OOPDS
44. Function Template Example
• Template version of Swap function:
template <typename T> Compiler produces 2
void Swap(T& x, T& y) { definitions of
T temp = x;
Swap() functions:
x = y;
y = temp;
one for int, one for
} string.
int main() {
Output:
int m = 22, n = 66;
Swap(m, n); // integers m = 66
cout << "m = " << m << endl n = 22
<< "n = " << n << endl; s1 = Kelly
string s1 = "Michael", s2 = "Kelly"; s2 = Michael
Swap(s1, s2); // strings
cout << "s1 = " << s1 << endl
<< "s2 = " << s2 << endl;
}
TCP1201 OOPDS
45. Declaring Function Template
• Function templates are declared in a similar as
ordinary functions, except that it is preceded
by the specification below:
template <typename T> // Less confusing, or
template <class T> // more confusing, class?
• Type parameter 'T' is used in place of ordinary
data types within the function definition.
• A template may have several type parameters:
template <typename T, typename U, typename V>
TCP1201 OOPDS
46. Declaring Class Template
• Class templates are declared in a similar way
as ordinary classes, except that it is preceded
by the specification below: template <typename T>
class Box {
T data;
...
• A class template may have several type
parameters: template <typename T, typename U>
class Box {
T data1;
U data2;
...
};
TCP1201 OOPDS
47. Class Template Example 1
Define a class template int main() {
Pair<int> p1;
Pair that have 2 attributes p1.set (3,5);
of the same type. Pair<string> p2;
p2.set ("Peter", "Jackson");
template <typename T> Pair<char> *p3 = new
Pair<char>;
class Pair { p3->set ('P','J');
T a; cout << p1.getA() << " "
T b; << p1.getB() << endl
public: << p2.getA() << " "
<< p2.getB() << endl
void set (T a, T b) { << p3->getA() << " "
this->a = a; << p3->getB();
this->b = b; delete p3;
} }
T getA() { return a; } Output:
T getB() { return b; } 3 5
}; Peter Jackson
TCP1201 OOPDS J
P
48. Class Template Example 1
Class template allows us to int main() {
Pair<int> p1;
write one class that works p1.set (3,5);
with different data types. Pair<string> p2;
p2.set ("Peter", "Jackson");
template <typename T> Pair<char> *p3 = new
Pair<char>;
class Pair { p3->set ('P','J');
T a; cout << p1.getA() << " "
T b; << p1.getB() << endl
public: << p2.getA() << " "
<< p2.getB() << endl
void set (T a, T b) { << p3->getA() << " "
this->a = a; << p3->getB();
this->b = b; delete p3;
} }
T getA() { return a; } Output:
T getB() { return b; } 3 5
}; Peter Jackson
TCP1201 OOPDS J
P
49. Class Template Example 2:
Multiple Type Parameters
Attributes may have int main() {
Pair<int,char> p1;
different data types. p1.set (1, 'A');
Pair<char,string> p2;
template <typename T,
p2.set ('B',"Boy");
typename U> Pair<int,int> *p3 = new
class Pair { Pair<int,int>;
T a; p3->set (99,99);
cout << p1.getA() << " "
U b;
<< p1.getB() << endl
public: << p2.getA() << " "
void set (T a, U b) { << p2.getB() << endl
this->a = a; << p3->getA() << " "
<< p3->getB();
this->b = b;
delete p3;
} }
T getA() { return a; }
U getB() { return b; } Output:
1 A
}; B Boy
TCP1201 OOPDS 99
99
50. Revisit STL vector Class
• The vector class provided by C++ Standard
Template Library (STL) is actually a class
template.
• vector is better than array when we need to
insert and/or remove data from a collection of
data, because vector can grow and shrink
automatically.
TCP1201 OOPDS
51. vector Example
...
#include <vector>
using namespace std;
class Student { ... };
int main() {
vector<Student> s; // vector of Student.
// Insert at back.
s.push_back (Student(22,"Bob")); // Bob
// Insert at beginning or index 0.
s.insert (s.begin(), Student(11,"Ali")); // Ali Bob
// Insert at index 1.
s.insert (s.begin()+1, Student(33,"Cat"));
for (int i = 0; i < s.size(); i++) // Ali Cat Bob
cout << s[i].getName() << " ";
cout << endl;
// Erase item at index 1.
s.erase (s.begin()+1); // Ali Bob
// Erase item at index 0.
s.erase (s.end()); // Ali
for (int i = 0; i < s.size(); i++)
cout << s[i] .getName()<< endl;
}
TCP1201 OOPDS