CISC 3142
Programming Paradigms in C++
Preliminaries

A review and examination of the following Java topics and concepts will ease the transition to programming in C++, as well as hopefully clearing things in Java as well

Java Compilation / Interpretation vs C++ Compilation / Linking / Execution

Java

C++

Scope and Lifetime

Scope

Java

C++

Lifetime (allocation / storage duration)

Java

C++

Categories of values, objects, variables

Java

C++

Reference and Value-semantics

Addresses the question of does assignment, passing parameters to a method, and/or returning from a method involve copying only the object's reference (reference semantics) or its entire value (value semantics)?

Aliases vs Copies

Java

Copy Constructors and Copy Methods in Java

Copy Constructor

class SomeClass {
	SomeClass(SomeClass srcParm) {	// constructor taking object of same class
		intVar = srcParm.intVar;				// or this.intVar = srcParm.intVar
		stringVar = srcParm.stringVar;	
	}
	…
	private int intVar;
	private String stringVar;
}

Usage:

SomeClass dest = new SomeCLass(src);

In-Place Copy Method

class SomeClass {
	…
	void copy(SomeCLass r1) {	// copy method taking object of same class
		intVar = r1.intVar;				// overwrites existing value of instance variable; again this.intVar = r1.intVar
		stringVar = r1.stringVar;	
	}
}

Two Less-Common Copy Methods

The following two methods also achieve a copy, however their logic doesn't parallel that of a constructor (the first one), or is not object-oriented (second).
'Cloning' Copy (Instance) Method
class SomeClass {
	…
	SomeClassvoid copy() {	// copy method taking object of same class
		SomeCLass result = new SomeClass();
		result.intVar = intVar1;				// or result.intVar = this.intVar
		result.stringVar = stringVar;	
	}
}

SomeClass dest = src.copy();		
or
dest = src.copy();		// redirects existing reference to new object
Static Copy Method
class SomeClass {
	…
	static void copy(SomeCLass destParm, SomeCLass srcParm) {	
		destParm.intVar = srcParmr1.intVar1;				
		destParm.stringVar = srcParmr1.stringVar;	
	}
}

copy(dest, src));		

A More Involved Example

We'll use the copy constructor and preferred copy method; i.e., the two standard methods of object-copying: creating a new copy and mutating (in-place modification) of an existing object.

class Integer {
	Integer(int val) {this.val = val;}
	Integer(Integer integer) {val = integer.val;}	// copy constructor
	…
	void copy(Integer integer) {val = integer.val;}	// in-place copy method
	…
	int val;
}
…
Integer i1 = new Integer(3);		// uses 'int' constructor
Integer i2 = new Integer(12);		// uses 'int' constructor
Integer i3 = new Integer(i1);		// uses copy constructor — clone
Integer i4 = i1;			// reference assignment — alias
i2.copy(i3);				// in-place copy method — clone
i2 = new Integer(i4);			// copy constructor — clone

C++

Mutability vs Immutability

Closely related to reference/value semantics is the notion of immutability

Some classes are deliberately designed so that their objects — once initialized — cannot be modified

Immutable Classes

class ImmutableColor {
	public ImmutableColor(int r, int g, int b) {
		this.r = r;
		this.g = g;
		this.b = b;
	}
	…
	public ImmutableColor lighter() {
		if (r < 255 && g < 255 && b < 255) 
			return new ImmutableColor(r+1, g+1, b+1)
		else
			return null;	
	}

	private final int r, g, b;
}

Notes

Mutable Classes

class MutableColor {
	public MutableColor(int r, int g, int b) {
		this.r = r;
		this.g = g;
		this.b = b;
	}
	…
	public MutableColor makeLighter() {
		if (r < 255 && g < 255 && b < 255)  {
			r++;
			g++;
			b++
			return this;
		}
		else
			return null;	
	}

	private int r, g, b;
}

Notes

Criteria for Mutability/Immutability

A Closer Look: String vs ArrayList

Parameter transmission

Basic Terminology

The caller passes arguments to the method where they are received in parameters

Call-by-Value

Java

The Problem with Swapping Two Values in Java

Swapping in Java

Using an Array
static void swap(int [] arr, int index1, int index2) {	
	int t = arr[index1];
	arr[index1] = arr[index2];
	arr[index2] = t;
}

in this scenario, we are not modifying the parameters — in particular the array reference — but rather elements of the object (i.e., the array) it references

Embedding the Values to be Swapped in an Object
class Pair {
	Pair(int first, int second) {
		this.first = first;
		this.second = second;
	}
	public int first, second;	// making them public so we can get to them - not relevant to the discussion
}

Pair pair = new Pair(a, b);
swap(pair);
a = p.first;
b = p.second;	

static void swap(Pair p) {	
	int t = p.first;
	p.first - p.second;
	p.second = t;
}

again, we are not modifying the parameter (the object reference), but rather what it is referencing — instance variables of the object (i.e., the Pair) it references

Objects and Arrays May be Changed, but It's still Call-By-value

int [] arr = {1, 2, 3};
inc(a);
…
void inc(int [] arr) {
	for i in 0; i < arr.length; i++)
	arr[i]++;
}

A Common Mistake: Modifying the Value of a Reference Parameter
The effect of modifying an object via indirection through a reference parameter is often mistaken (and referred to) as call-by-reference. There are probably two reasons for this: While it's true that the reference parameter is what supplies the indirection and thus the modification of the object, the reference itself is what is being passed and THAT is still being passed by-value (i.e., as a copy of the argument).
int [] arr = {1, 2, 3};
resize(a);
…
void resize(int [] arr) {
	int [] temp = new int [arr.length * 2];
	for (int i = 0; i < arr.length; i++)
		temp[i] = arr[i];
	arr = temp;
}
The Right way

C++

Generics

generic programming

Java

C++

vector<int> i_vect;
i_vect.add(12);
…
vector<string> s_vect;
s_vect.add("Hello");
				

Polymorphism

Overloading and Overriding

overloading (compile time / static polymorphism) vs. overriding (run time / dynamic polymorphism)

Overloading (Compile-time Polymorphism)

Methods can be defined with the same name (in the same scope) as long as they differ by number and/or types of parameters.
class C {
	…
	void print(int i) {…}
	void print(String s) {…}
	…
}

Overriding (Run-time Polymorphsim)

Methods within a subclass can be defined with the same name and type signature as a method in a superclass; the subclass method will be invoked when messages are passed to a subclass object (even if through a superclass variable).
class C {
	…
	void f(int i) {…}
	…
}
…
class D extends C {
	…
	void f(int i) {…}
	…
}

Maintaining Semantic Consistency

Semantic consistency: logically related concepts should should have the same meaning / treated the same way. We can achieve semantic consistency across methods, by being disciplined in our coding. The most common way to do this is by having methods that should be semantically consistent share code. This is done by leveraging functionality

Leveraging Functionality

Delegation / Wrapper Methods

Example: size and isEmpty

Example: Composition of toString Methods

Example: Copy Constructors and Copy Methods

Example: Immutable and Mutable Arithmetic Operators

A Related Example: Mutable and Immutable Methods of a Rational Class

rational number: a number that can be represented as the ratio of two integers; i.e., a number that can be represented using a numerator and denominator; a fraction.
class Rational {
	…
	Rational mul(Rational r) {…}		
	Rational mulInPlace(Rational r) {…}
	…
	int num, denom;
}
Rational 
	r1 = new Rational(1, 2),
	r2 = new Rational(3, 4);

Rational r3 = r1.mul(r2);	// r3 is pointing at a new Rational containing 3/8; r1 and r2 are unchanged
r1.mulInPlace(r2);		// r1 is still pointing at the same object as before, but the num/denom is now 3/8
r2 = r2.mul(r2);		// r2 is pointing at a new (different) Rational object containing 9/16

Deciding on Who Leverages Whom

Sometimes one has no choice: However, one often has a choice as to which function is leveraged (i.e., which is the workhorse and which the wrapper):

Example: The Rational class again

The semantics of rational number multiplication is to multiply the numerators and denominators, i.e.,
a     c     a * c
-  *  -  =  -----
b     d     b * d
No Leveraging
I.e., both methods perform the multiplication logic … this 'no leveraging example' is just for illustrative purposes; typically one method would leverage the other, as shown in the next two sections.
mul Method
Rational mul(Rational r) {
	Rational result = new Rational(this.num * r.num, this.denom * r.denom);
	return result;
}
mulInPlace Method
Rational mulInPlace(Rational r) {
	this.num *= r.num;		
	this.denom *= r.denom;
	return this;
}
mulInPlace Leveraging mul
mul Leveraging mulInPlace
More on mulInPlace's Return Value

Initialization of Variables in the Presence of Composition and Inheritance

General Philosophy

Java

C++

Composition

Java

C++

Inheritance

Java

C++

Code Used in this Lecture