Previous Lecture Lecture 3 Next Lecture

Lecture 3, Tue 04/09

Class Design

Classes

A note about C++ structs

Example - definition of a class representing a Person

# Makefile

CXX=g++
DEPENDENCIES=main.o Person.o

main: ${DEPENDENCIES}
	${CXX} -o $@ -std=C++11 $^

clean:
	rm -f *.o main
// Person.h
#ifndef PERSON_H
#define PERSON_H

#include <iostream>

// Interface for class representing a Person

class Person {
	public:
		Person(); 			// default constructor
		Person(string name, int age);	// overloaded constructor
		Person(Person& person);		// copy constructor
		string getName() const; 	// accessor / getter
		int getAge() const; 		// accessor / getter
		void setName(string name); 	// mutator / setter
		void setAge(int age);		// mutator / setter

	private:
		string name;
		int age;
};

#endif

Note: You should not use using namespace in your header files. This forces consumers of the class to use the namespace, which may not be intended or expected.

// Person.cpp
#include <iostream>
#include <string>
#include "Person.h"

using namespace std;

// default constructor
Person::Person() {
	name = "-";
	age = 0;
	cout << "Default Constructor" << endl;
}

// constructor overloading
Person::Person(string name, int age) {
	this->name = name;
	this->age = age;
}

// copy constructor
Person::Person(Person& person) {
	name = person.getName();
	age = person.getAge();
	cout << "copy constructor" << endl;
}

string Person::getName() const { return name; }

int Person::getAge() const { return age; }

void Person::setName(string name) { this->name = name; }

void Person::setAge(int age) { this->age = age; }
// main.cpp

#include <iostream>
#include "Person.h"

using namespace std;

int main() {
	Person p;
	return 0;
}

Public vs. Private

Abstract Data Types (ADTs):

Scope Resolution Operator (::)

Accessor and Mutator Functions

Constructors

Copy Constructor

Shallow Copy illustration

// modify class Person.h definition with vector v
public:
vector<int>* getVector() const;

private:
vector<int>* v;
// modify constructor in Person.cpp to initialize vector and push 100 into it
Person::Person() {
	name = "default name";
	age = 0;
	v = new vector<int>();
	v->push_back(100);
}

// Accessor function for the vector v
vector<int>* Person::getVector() const {
	return v;
}
// main.cpp

// function to print out contents of the vector
template<class T>
void printVector(vector<T> v) {
	for (int i = 0; i < v.size(); i++) {
		cout << v[i] << endl;
	}
}

// in main()
s.getVector()->push_back(200);
printVector(*s.getVector()); // 100 200
printVector(*t.getVector()); // 100 200
Person::Person(Person& person) {
	name = person.getName();
	age = person.getAge();
	v = new vector<int>();
	
	// DEEP COPY
	for (int i = 0; i < person.getVector()->size(); i++) {
		v->push_back(person.getVector()->at(i));
	}
	cout << "copy constructor" << endl;
}

Example of Overloading the Assignment Operator

// Person.h
Person& operator=(const Person& rhs);
// Person.cpp
Person& Person::operator=(const Person& rhs) {
	cout << "overloaded assignment operator" << endl;
	
	// check self-assignment, p1 = p1
	if (this == &rhs) {
		return *this;
	}
	this->name = rhs.name;
	this->age = rhs.age;
	this->v->clear();

	for (int i = 0; i < rhs.v->size(); i++) {
		v->push_back(rhs.v->at(i));
	}
	return *this;
}

Copy Constructor vs. Assignment Operator

int main() {
	Person s;
	cout << "&s = " << &s << endl;
	Person t = s; // copy constructor
	cout << "&t = " << &t << endl;
	cout << "---" << endl;
	Person a;
	Person b;
	a = b;	// overloaded assignment operator
}

Destructor

// Person.h
~Person(); 	// destructor
// Person.cpp
Person::~Person() {
 	delete v; // delete vector that should exist on the heap.
 	cout << "DELETED: v" << endl;
}

Example illustrating destructor call when exiting function

// main.cpp
#include <iostream>
#include "Person.h"

using namespace std;

void f() {
	Person* p = new Person(); // default constructor assigns object on the heap
	delete p; 	// manually call default constructor
				// Memory leak in the heap if call is not made.

	// Person p; // default constructor on the stack
	// when function exits, invokes destructor for objects on the stack.
}

int main() {
	f();
	cout << "exiting main..." << endl;
	return 0;
}

Big Three (Rule of Three)