Exercises (do NOT turn in)

* Ch 3: 3.3(a,b,d,g), 3.4.

* You are strongly encouraged to do other exercises too.

Reading Assignment

* Ch 3.4-end of chapter.

Abstract Data Types (ADTs)

* This is the central theme of this course.

* Data Abstraction = the separation of the properties of a data type (its values and operations) from the implementation of that data type.

* As with functional abstraction (last week's lecture), data abstraction employs a specification and an implementation.

* The specification describes:

1. the properties of the "data values", and

2. the behavior of each of the "operations" on those values.

1. How to implement an ADT.

* choose a concrete "data representation" of the ADT, using data types that already exist

* implement each allowable operation in terms of program instructions

* In Ada, a language taught in this class last year, we can use PACKAGES to implement ADTs.

* In C++, we can use "classes".

* A class is a "programmer-defined type" that is used for creating abstract data types.A

2. An example - the Password class

class Password {

char pass_str[11];

public:

void get_password();

Boolean valid_user();

};

1. Inside a class, you can declare variables (known as "data members) and functions (known as "member functions") as usual.

2. Members declared after the keyword "public" belong to the public interface.

3. Members declared after the keyword "private" belong to the private interface.

4. By default, private interface is assumed.

5. Members in the private interface are "hidden" from clients. The compiler will not allow the client code to access a private item.

6. Members in the public interface can be used by the clients directly.

* In the Password class example (previous slide), tell me:

1. How many data members? private? public?

2. How many member functions? private? public?

3. What can be used by clients directly?

3. The same example, with better layout:

class Password {

public:

void get_password();

Boolean valid_user();

private:

char pass_str[11];

};

* Better because it

1. highlights the public interface

2. deemphasize the private data representation

3. use explicit "public" or "private" keywords to avoid confusion.

4. You should use this style for all your work.

4. How clients use a class.

* just treat the class as a programmer-defined type.

1. declare variables

Password pw1, pw2;

2. specify the type of an argument

void decipher(Password pw);

3. use it to build other types

class Super_Password {

public:

...

private:

Password my_pw;

...

};

4. and more...

5. Another example - the Jar_Type class

class Jar_Type {

public:

void init();

void add(int n);

int quantity() const;

private:

int units;

};

1. Note a special use of "const" after the quantity() member function.

6. A class is a type.

Jar_Type jar1;

Jar_Type jar2, jar3;

1. The code above creates 3 instances of the Jar_Type class.

2. Each instance has its own copy of "units", the private data member of the class.

3. These three copies of "units" may contain different values at any given time.

7. Operations on classes

* Most of built-in operations do NOT apply to classes.

* For example:

1. You cannot use the "+" operator to add two Jar_Type instances.

2. You cannot use the "==" operator to compare two Jar_Type instances for equality.

* Two built-in operations that are valid for class instances are member selection "." and assignment "=". Here are some examples:

jar1.init();

jar3 = jar1;

Jar_Type jar1, jar2;

jar1.init();

jar1.add(10);

// jar1 now has ? units

Jar_Type jar3 = jar1;

// jar3 now has ? units

jar3.init();

// jar3 now has ? units

// jar1 now has ? units

jar3.add(20);

jar2 = jar1;

// jar2 now has ? units

if (jar2.quantity() == jar3.quantity()) {

cout << "Hello.\n";

}

8. Class scope

* The name of a class member has "class scope" - the name is local to the class. The same name can be used in other classes.

class A {

void printme();

};

class B {

void printme();

};

void printme(); // this is a global function

A a;

B b;

a.printme();

b.printme();

printme();

9. Information hiding

* The "private" access modifier supports information hiding.

Jar_Type jar1;

jar1.units = 200; // No! cannot change units

cout << jar1.units; // No! cannot see units

jar1.init(); // okay?

jar1.add(200); // okay?

cout << jar1.quantity(); // okay?

10. ADT, Module, Specification, Implementation

* The Jar_Type class is an ADT, why?

* In C++, it is customary to package a class into a module.

* The specification ".h" file contains the class declaration.

* The implementation ".cpp" file contains the definitions of the member functions.

// jar.h

#ifndef JAR_H

#define JAR_H

class Jar_Type {

public:

void init();

// POST: number of units in jar is 0.

void add(int n);

// PRE: init() has been invoked, and n >= 0.

// POST: n units have been added to jar.

int quantity() const;

// PRE: init() has been invoked.

// POST: return number of units in jar.

private:

int units; // number of units in jar

};

* Notice the preconditions for the add() and quantity() functions. We will come back to this point later.

// jar.cpp

#include "jar.h"

void

Jar_Type::init()

{

units = 0;

}

void

Jar_Type::add(int n)

{

units += n;

}

int

Jar_Type::quantity() const

{

return units;

}

11. Constructors

* What was the problem with the Jar_Type class?

* The client of the class may "forget" (don't we all forget most of the time?) to invoke init() before using a class instance.

* C++ provides a mechanism, called a "constructor", to guarantee the initialization of a class instance.

* A constructor is a "member function" that is "implicitly invoked" whenever a class instance is created.

* A constructor has the same name as the class itself.

* A new Jar_type class:

class Jar_Type {

public:

Jar_Type(); // default constructor

Jar_Type(int n); // another constructor

void add(int n);

int quantity() const;

private:

int units;

};

12. Implementation for the constructors

Jar_Type::Jar_Type(int n)

{

units = n;

}

Jar_Type::Jar_Type()

{

units = 0;

}

13. Invoking a constructor

* A constructor is NEVER invoked using the dot notation.

* A constructor is invoked whenever a class instance is created.

* Explicit invocation: Jar_Type(int)

Jar_Type jar1 = Jar_Type(20);

* Implicit invocation: Jar_Type(int)

Jar_Type jar2(20);

* Implicit invocation: Jar_Type() - default constructor

Jar_Type jar3;

cout << "enter the initial number of items: ";

cin >> init_count;

Jar_Type my_jar(init_count);

cout << "how many more items do you wish to add? ";

cin >> item_count;

my_jar.add(item_count);

14. Guidelines for use of class constructors

* A constructor cannot return a function value, so the function must be declared without a data type.

* A class may provide several constructors. When a class instance is declared, the compiler will choose the appropriate constructor according to the types and ordering of the arguments.

* If a class has no constructors at all, C++ generates a simple "default" constructor (that simply allocates memory) for the object.

* If a class has some "non-default" constructors, then NO "default" constructor will be generated.

* If an array of class instances is declared:

Some_Class my_array[100];

* then a default constructor is required. This constructor will be invoked for each element of the array.