COMP2012 Object-Oriented Programming and Data Structures
Topic 1: Revision Example, Pointer, Reference & Const-ness
Dr. Desmond Tsoi
Department of Computer Science & Engineering The Hong Kong University of Science and Technology Hong Kong SAR, China
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 1 / 31
Why Take This Course?
You have taken COMP1021/1022P/1022Q and COMP2011. So you can program already, right?
Think about this: You have been learning English for many years, but can you write a novel?
You basically have learned the C part of C++ in COMP2011 with a brief introduction to C++ classes, and you can write small C++ programs.
But what if you are to write a large program, probably with a team of programmers?
In this course, you will learn the essence of OOP with some new C++ constructs with an aim to write large softwares.
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 2 / 31
Part I
A Revision Example: Person and Family
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 3 / 31
A Revision Example: Person & Family
It consists of a the class Person, from which families are built.
A person, in general, has at most 1 child, and his/her father and
mother may or may not be known.
The information of his/her family includes him/her and his parents and grandparents from both of his/her parents.
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 4 / 31
Revision Example: Expected Output
Name: Arthur
Father: unknown
Mother: unknown
Grand Fathers: unknown, unknown
Grand Mothers: unknown, unknown
Name: Becky
Father: unknown
Mother: unknown
Grand Fathers: unknown, unknown
Grand Mothers: unknown, unknown
Name: Claire
Father: Arthur
Mother: Becky
Grand Fathers: unknown, unknown
Grand Mothers: unknown, unknown
Name: Eddy
Father: unknown
Mother: Claire
Grand Fathers: unknown, Arthur
Grand Mothers: unknown, Becky
Rm 3553, desmond@ust.hk
COMP2012 (Fall 2020) 5 / 31
Revision Example: Person Class — Header File
#include
using namespace std;
class Person
{
/* File: person.h */
private:
char* _name;
int _age;
Person *_father, *_mother, *_child;
public:
Person(const char* my_name, int my_age, Person* my_father = nullptr,
Person* my_mother = nullptr, Person* my_child = nullptr);
̃Person();
Person* father() const;
Person* mother() const;
Person* child() const;
void print_age() const;
void print_name() const;
void print_family() const;
void have_child(Person* baby);
};
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 6 / 31
Revision Example: Person Class — Implementation File I
#include “person.h” /* File: person.cpp */
#include
Person::Person(const char* my_name, int my_age, Person* my_father,
Person* my_mother, Person* my_child)
{
_name = new char [strlen(my_name)+1];
strcpy(_name, my_name);
_age = my_age;
_father = my_father;
_mother = my_mother;
_child = my_child;
};
Person:: ̃Person() { delete [] _name; }
Person* Person::father() const { return _father; }
Person* Person::mother() const { return _mother; }
Person* Person::child() const { return _child; }
void Person::have_child(Person* baby) { _child = baby; }
void Person::print_age() const { cout << _age; }
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 7 / 31
Revision Example: Person Class — Implementation File II
void Person::print_name() const
{
cout << (_name ? _name : "unknown");
}
// Helper function
void print_parent(Person* parent)
{
if (parent)
parent->print_name();
else
cout << "unknown";
}
Rm 3553, desmond@ust.hk
COMP2012 (Fall 2020) 8 / 31
Revision Example: Person Class — Implementation File III
void Person::print_family() const
{
Person *f_grandfather = nullptr, *f_grandmother = nullptr,
*m_grandfather = nullptr, *m_grandmother = nullptr;
if (_father) {
f_grandmother = _father->mother();
f_grandfather = _father->father();
}
if (_mother) {
m_grandmother = _mother->mother();
m_grandfather = _mother->father();
}
cout << "Name: "; print_name(); cout << endl;
cout << "Father: "; print_parent(_father); cout << endl;
cout << "Mother: "; print_parent(_mother); cout << endl;
cout << "Grand Fathers: "; print_parent(f_grandfather);
cout << ", "; print_parent(m_grandfather); cout << endl;
cout << "Grand Mothers: "; print_parent(f_grandmother);
cout << ", "; print_parent(m_grandmother); cout << endl;
}
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 9 / 31
Revision Example: Family Building Test Program
#include "person.h" /* File: family.cpp */
int main() {
Person arthur("Arthur", 65, nullptr, nullptr, nullptr);
Person becky("Becky", 63, nullptr, nullptr, nullptr);
Person claire("Claire", 32, &arthur, &becky, nullptr);
Person eddy("Eddy", 4, nullptr, &claire, nullptr);
arthur.have_child(&claire);
becky.have_child(&claire);
claire.have_child(&eddy);
arthur.print_family(); cout << endl;
becky.print_family(); cout << endl;
claire.print_family(); cout << endl;
eddy.print_family(); cout << endl;
return 0;
}
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 10 / 31
Part II
Reference and Pointer
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 11 / 31
Variable, Reference Variable, Pointer Variable
#include
using namespace std;
int x = 5;
int& xref = x;
int* xptr = &x;
void xprint()
{
// An int variable
// A reference variable: xref is an alias of x
// A pointer variable: xptr points to x
cout << hex << endl; // Print numbers in hexadecimal format
cout << "x = " << x << "\t\tx address = " << &x << endl;
cout << "xref = " << xref << "\t\txref address = " << &xref << endl;
cout << "xptr = " << xptr << "\txptr address = " << &xptr << endl;
cout << "*xptr = " << *xptr << endl;
}
int main() {
x += 1; xprint();
xref += 1; xprint();
xptr = &xref; xprint(); // Now xptr points to xref
return 0; }
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 12 / 31
Pointer vs. Reference
Reference can be thought as a special kind of pointer, but there are 3 big differences:
1. A pointer can point to nothing (nullptr), but a reference is always bound to an object.
2. A pointer can point to different objects at different times (through assignments). A reference is always bound to the same object.
Assignments to a reference does not change the object it refers to but only the value of the referenced object.
3. The name of a pointer refers to the pointer object. The * or -> operators have to be used to access the object.
The name of a reference always refers to the object. There are no special operators.
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 13 / 31
This Pointer
Each class member function implicitly contains a pointer of its class type named “this”.
When an object calls the function, this pointer is set to point to the object.
For example, after compilation, the member function Person::have child(Person* baby) of Person will be translated to a unique global function by adding a new argument:
void Person::have_child(Person* this, Person* baby)
{
this->_child = baby;
}
The call, becky.have child(&eddy) becomes Person::have child(&becky, &eddy).
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 14 / 31
Return an Object by this — complex.h
class Complex /* File: complex.h */
{
private:
float real; float imag;
public:
Complex(float r, float i) { real = r; imag = i; }
void print() const { cout << "(" << real << " , " << imag << ")\n"; }
Complex add1(const Complex& x) // Return by value
{
real += x.real; imag += x.imag;
return (*this);
}
Complex* add2(const Complex& x) // Return by value using pointer
{
real += x.real; imag += x.imag;
return this;
}
Complex& add3(const Complex& x) // Return by reference
{
real += x.real; imag += x.imag;
return (*this);
}
};
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 15 / 31
Return an Object by this — complex-test.cpp
#include
using namespace std;
#include “complex.h”
void f(const Complex a) { a.print(); } // const Complex a = u
void g(const Complex* a) { a->print(); } // const Complex* a = &u
void h(const Complex& a) { a.print(); } // const Complex& a = u
int main() {
// Check the parameter passing methods
Complex u(4, 5); f(u); g(&u); h(u);
// Check the parameter returning methods
Complex w(10, 10); cout << endl << endl;
Complex x(4, 5); (x.add1(w)).print(); // Complex temp = *this = x
Complex y(4, 5); (y.add2(w))->print(); // Complex* temp = this = &y
Complex z(4, 5); (z.add3(w)).print(); // Complex& temp = *this = z
cout << endl << endl; // What is the output now?
Complex a(4, 5); a.add1(w).add1(w).print(); a.print(); cout << endl;
Complex b(4, 5); b.add2(w)->add2(w)->print(); b.print(); cout << endl;
Complex c(4, 5); c.add3(w).add3(w).print(); c.print();
return 0;
}
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020)
16 / 31
Return-by-Value and Return-by-Reference
There are 2 ways to pass parameters to a function pass-by-value (PBV)
pass-by-reference (PBR)
lvalue reference: that is what you learned in the past and we’ll keep just saying reference for lvalue reference.
rvalue reference (C++11)
Similarly, you may return from a function by returning an object’s
value: the function will make a separate copy of the object and return it. Changes made to the copy have no effect on the original object.
(lvalue) reference: the object itself is passed back! Any further operations on the returned object will directly modify the original object as it is the same as the returned object.
rvalue reference: we’ll talk about this later.
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 17 / 31
Part III
const-ness
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 18 / 31
const
const, in its simplest usage, is to express a user-defined constant — a value that can’t be changed.
const float PI = 3.1416;
Some people like to write const identifiers in capital letters.
In the old days, constants are defined by the #define preprocessor directive:
#define PI 3.1416 Question: Any shortcomings?
const actually may be used to represent more than just numerical constants, but also const objects, pointers, and even member functions!
The const keyword can be regarded as a safety net for programmers: If an object should not change, make it const.
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 19 / 31
Example: Constant Object of User-defined Types
/* File: const-object-date.h */
class Date // There are problems with this code; what are they?
{
private:
int year, month, day;
public:
Date() { cin >> year >> month >> day; }
Date(int y, int m, int d) { year = y; month = m; day = d; }
void add_month() { month += 1; }; // Will be an inline function
int difference(const Date& d)
{ /* Incomplete: write this function */ }
void print()
{ cout << year << "/" << month << "/" << day << endl; }
};
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 20 / 31
Example: Constant Object of User-defined Types ..
#include
using namespace std;
#include “const-object-date.h”
int main() // There are problems with this code; what are they?
{
const Date WW2(1945, 9, 2); // World War II ending date
Date today;
WW2.print();
today.print();
// How long has it been since World War II?
cout << "Today is " << today.difference(WW2)
<< " days after WW2" << endl;
// What about next month?
WW2.add_month(); // Error; do you mean today.add_month()??
cout << today.difference(WW2) << " days by next month.\n";
return 0; }
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 21 / 31
const Member Functions
To indicate that a class member function does not modify the class object — its data member(s), one can (and should!) place the const keyword after the argument list.
class Date /* File: const-object-date2.h */
{
private:
int year, month, day;
public:
Date() { cin >> year >> month >> day; }
Date(int y, int m, int d) { year = y; month = m; day = d; }
void add_month() { month += 1; }; // Will be an inline function
int difference(const Date& d) const { /* Incomplete */ }
void print() const
{ cout << year << "/" << month << "/" << day << endl; }
};
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 22 / 31
const Member Functions and this Pointer
A const object can only call const member functions of its class. But a non-const object can call both const and non-const member
functions of its class.
The this pointer in const member functions points to const objects.
For example,
int Date::difference(const Date& d) const; is compiled to
int Date::difference(const Date* this, const Date& d);
void Date::print() const; is compiled to
void Date::print(const Date* this);
Thus, the object calling const member function becomes const inside
the function and cannot be modified.
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 23 / 31
const and const Pointers
When a pointer is used, two objects are involved:
the pointer itself
the object being pointed to
The syntax for pointers to constant objects and constant pointers can be confusing. The rule is that
any const to the left of the ∗ in a declaration refers to the object being pointed to.
any const to the right of the ∗ refers to the pointer itself. It can be helpful to read these declarations from right to left.
/* File: const-char-ptrs1.cpp */
char c = 'Y';
char *const cpc = &c;
char const* pcc;
const char* pcc2;
const char *const cpcc = &c;
char const *const cpcc2 = &c;
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 24 / 31
Example: const and const Pointers
#include
using namespace std;
int main() {
char s[] = “COMP2012”; // Usual initialization in the past
char p[] {“MATH1013”}; // C++11 style of uniform initialization
}
const char* pcc {s};
pcc[5] = ‘5’;
pcc = p;
char *const cpc = s;
cpc[5] = ‘5’;
cpc = p;
// Pointer to constant char
// Error!
// OK, but what does that mean?
// Constant pointer
// OK
// Error!
const char *const cpcc = s; // const pointer to const char
cpcc[5] = ‘5’;
cpcc = p;
return 0;
// Error!
// Error!
Rm 3553, desmond@ust.hk
COMP2012 (Fall 2020)
25 / 31
const and const Pointers ..
Having a pointer-to-const pointing to a non-const object doesn’t make that object a constant!
/* File: const-int-ptr.cpp */
int i = 151;
i += 20; // OK
int* pi = &i; *pi+=20; //OK
const int* pic = &i;
*pic += 20; // Error! Can’t change i through pic
pic = pi; // OK
*pic += 20; // Error! Can’t change *pi thru pic
pi = pic; // Error: Invalid conversion from ‘const int*’ to ‘int*’
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 26 / 31
const References as Function Arguments
There are 2 good reasons to pass an argument as a reference. What are they?
You can (and should!) express your intention to leave a reference argument of your function unchanged by making it const.
There are 2 advantages:
1. If you accidentally try to modify the argument in your function, the compiler will catch the error.
void cbr(int& x) { x += 10; } // Fine
void cbcr(const int& x) { x += 10; } // Error!
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 27 / 31
const References as Function Arguments ..
2.
You may pass both const and non-const arguments to a function that requires a const reference parameter.
Conversely, you may pass only non-const arguments to a function that requires a non-const reference parameter.
#include
using namespace std;
void cbr(int& a) { cout << a << endl; }
void cbcr(const int& a) { cout << a << endl; }
int main() {
int x {50}; const int y {100};
// Which of the following give(s) compilation error?
cbr(x);
cbcr(x);
cbr(y);
cbcr(y);
cbr(1234);
cbcr(1234);
}
Rm 3553, desmond@ust.hk
COMP2012 (Fall 2020)
28 / 31
Summary: Good Practice
Objects you don’t intend to change ⇒ const objects const double PI = 3.1415927;
const Date handover(1, 7, 1997);
Function arguments you don’t intend to change
⇒ const arguments
void print_height(const Large_Obj& LO){ cout << LO.height(): }
Class member functions don’t change the data members
⇒ const member functions
int Date::get_day() const { return day; }
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 29 / 31
Summary
Regarding which objects can call const or non-const member functions:
Calling Object
const Member Function
non-const Member Function
const Object non-const Object
√ √
X
√
Regarding which objects can be passed to functions with const or non-const arguments:
Passing Object
const Function Argument
non-const Function Argument
literal constant const Object non-const Object
√ √ √
X
X
√
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 30 / 31
That’s all! Any questions?
Rm 3553, desmond@ust.hk COMP2012 (Fall 2020) 31 / 31