Notes on Classes in C++

Classes in C++ #

Access Restrictions: #

  • public:

    • the data/method is accessible to all methods and theowner of the class variable
  • private:

    • data/method is only accessible to methods but not tothe object owner
  • protected:

    • similar to private, used with class inheritance. Methodof derived class have access, but the object owner doesnot. (We will discuss this in more detail later)

Example:

class A
{
public:
	// Public data/methods
	int x;
	void foo() { x++; y--; }

private:
	// Private data/methods
	int y;
	void bar() { x--; y++; }
};

Constructors: #

  • Class variables can be automatically initialized by con-structors
  • No uninitialized struct variables anymore! Thisis a major improvement over C
  • If not defined, the compiler creates the DEFAULT con-structor for you. It does not initialize POD members, but calls sub-object constructors recursively

Example:

class Foo
{
public:
	Foo() { x = 0; }          // constructor 1
	Foo(int x_) { x = x_; }   // constructor 2

private:
	int x;
};

Destructors: #

  • A destructor is called whenever a class variable leaves the scope or is deleted. Automatic cleanup!
  • If we don’t define a destructor, the compiler creates a default destructor for us, which only calls the destruc-tors of all non-POD members
  • The destructor must be defined whenever the class ob-ject allocates resources (memory, files, locks …) that need to be freed when the object is no longer needed

Example:

class Foo
{ 
public:
// automatically allocate array when a
// Foo is created
Foo()  { p = new int[100]; }

// destructor: clean up when done
// name:  ~Classname
~Foo() { delete [] p; }

private:
	int *p;
};

Copy Constructors (CC): #

  • It would be a waste of time if we first call the con-structor and then overwrite the result with the stateof another object
  • Also, simply copying data members bitwise may notwork. For instance, if we just copied pointers, bothpointers in a and b would point to the same object, i.e. they share a resource. Often this is not accept-able.

Example:

class Foo
{
public:
	Foo() { x = y = 0; }

	// this is what the default CC does:
	// data members are copied one by one
	// from the rhs object to the lhs object
	// NB.: if class contains class variables
	// their copy constructors are called
	// recursively
	Foo(const Foo &rhs)
	{
		x = rhs.x;
		y = rhs.y;
	}
private:
	int x, y;
};

Foo a;
Foo b = a; // rhs=a; effect: b.x = a.x, b.y = a.y
  • Watch out! Pointers are POD types, which are copied bitwise
  • After copying, pointee object is shared by the lhs and rhs objects!
  • Who then is responsible for deleting the shared ob-ject?
  • If sharing resources when copy constructing your ob-jects isn’t what you want, then you need to defineyour own copy constructo

Assignment Operators: #

How to define the AO for class Foo?

a = b;

/

is transcribed by the compiler into

a.operator=(b)

object to act on: left-hand-side (lhs) a, right-hand-side (rhs) b passed to method operato.

So, the AO can be considered a method!

Example:

class Foo
{
	int u;
	Foo() { u = 0; }
	// assignment operator, pass on a reference to the object
	Foo &operator= (const Foo &rhs)
	{
		u = rhs.u;    // for POD members, just copy bitwise
		return *this; // returns a reference to the lhs object
									// itself. "this" points to the object
									// itself and it is implicitely known in
									// all methods
	}
};

Note: this is a pointer to the object and *this refers to the object itself. So, method

Foo f() { return *this; }

returns a copy of the object. But

Foo &f() { return *this; }

just returns a reference the object itself (much faster)

The default AO, which is created if you don’t provide one, does member-by-member copy

  • Bitwise copy for POD members and calling the assignment operators for all other data members
  • This may not be what you want if the class has pointer members! (see sharing issues discussed inthe CC section)
  • You can provide your own AO for each class if the default AO is insufficient
Foo a(10);
a = a

In above implementation first releases memory associated with a, and then uses it.

Ouch! It may have changed in the interim. So, we need to guard against self assignment!

Example:

class X
{
public:

	// general assignment operator code template
	X &operator= (const X &rhs)
	{
		if (this == &rhs) {  // self-assignment,
			return *this;      // nothing to do!
		}                    // release current resources and copy rhs
	...
	return *this;
	}
};

Copy:

The space we copy to does not contain anything,so we can just overwrite it

X a = b; // defining a, just copy b over

Assignment:

the space we copy to is occupied. We mayhave to release resources before copying

a = b; 
// if a contains a resource its assignment
// operator must first release it before copying members

Shallow vs. Deep Copy: #

  • In the presence of pointer data members we have to decide how to copy objects
  • If we allow to share resources, the default CC and AO will work fine — as they copy bits in case of POD members (shallow copy) and call the CC/AO for non-POD types recursively
  • Otherwise we need to copy the data the pointer points to recursively (deep copy)
    • In this case, we need to implement both CC and AO,and most likely the destructor as well
  • Make sure that there are no resource leaks and no self-assignments!

Rule of 3:

if you decide you need to define your own destructor, CC, or AO, you most likely also have to define the other two