Sie sind auf Seite 1von 26

Ve280 Notes

Ve280 Notes
Linux Commands
Developing C++ Programs
C++ Basics Review
Const
Procedural Abstraction
Recursion
Function Pointers
Call Stacks
Enumeration
Program Arguments
I/O
Testing
Exceptions
Abstract Data Types
Subtypes and Inheritance
Interfaces and Invariants
Dynamic Memory Allocation and Destructors
Deep Copy
Dynamic Resizing
Linked Lists
Templates and Containers
Operator Overloading
Linear Lists and Stack
Queues
Standard Template Library: Sequential Containers
Standard Template Library: Container Adaptors and Associative Containers

Linux Commands
cd - used to change directory

. - refers to current directory


.. - refers to parent directory
~ - refers to the home directory (oftentimes the user's directory)
/ - refers to the root directory
ls - used to list contents of current directory

ls -a - list all, including hidden files (like .files)


ls -l - list in long format, including extra file information

Follows format:

permissions # of links owner group file size last modification file name

ls -t - sort by time and date


mkdir <dir> - creates directory

rmdir<dir> - remove directory

can only remove if empty


touch <file.txt> - create empty file
cp <source> <dest> - copies source to destination

cp -r - copy recursively, i.e copy source as dest if dest doesn’t exist


can copy file file, file directory, or directory directory
can copy multiple files/directories to destination
* - wildcard

Ex.) ls *.xyz returns all files with .xyz extension


mv - move or rename files and directories

rm - removes files

rm -r - delete directory along with contents


rm -i - prompts before deleting
cat - prints out file

less - allows user to read file (uses vim navigation :D)

> - redirection of output

Ex.) ls -l > ls_rst.txt - writes ls output to ls_rst.txt


< - redirection of input

Ex.) my_add < input.txt feeds contents of input.txt to program my_add


diff <file1> <file2> - outputs differences between files

sudo - executes as superuser

sudo apt-get install <program>


sudo apt-get install autoremove <program>
man <command> - see manual for help with command

Developing C++ Programs


To slides

Basic working mechanism of a computer:

We generally use g++ -o <program> <src1.cpp> <src2.cpp> to compile a program.

with -Wall : returns all warnings - useful for catching errors


with -g : puts debugging info in the executable file

This is equivalent to first generating an object file using g++ -c <source.cpp> and then linking
the object file into the machine language code with g++ -o <program> <source.o> .

[C++ source code] [Object code] [machine language]

Splitting a project into multiple files allows us to recompile only one file for a small
change.

Source Files
There are 2 types of files:
header files ( .h files) - normally contain class definitions and function declarations
c++ source files ( .cpp files) - normally contain function definitions and member
functions of classes.

Example

1 // add.h
2 #ifndef ADD_H
3 #define ADD_H
4 int add(int a, int b);
5 #endif

1 // add.cpp
2 int add(int a, int b)
3 {
4 return a+b;
5 }

If we want to use the function add() in another file, we put #include "add.h" at the top.

Header Guard
Sometimes a header file may included by multiple source files. To avoid redefinition of classes
and functions, we use a header guard:

1 // add.h
2 #ifndef ADD_H
3 #define ADD_H
4 int add(int a, int b);
5 #endif

Here, ADD_H is a preprocessor variable. If not already defined by some other header file, then it
will be defined here.

All header files should use header guards

*Note: header files aren't included when compiling with g++ because their connected .cpp file
automatically includes them

Makefiles
Instead of generating each object file separately and then linking every time, we can use a
makefile.

The general format of a make file is as follows

1 Target: Dependency
2 Command

We run the command make to compile

Example
1 all:run_add # <- the default target
2
3 run_add:run_add.o add.o
4 g++ -o run_add run_add.o add.o
5
6 run_add.o:run_add.cpp
7 g++ -c run_add.cpp
8
9 add.o:add.cpp
10 g++ -c add.cpp
11
12 clean:
13 rm -f run_add *.o

C++ Basics Review


To slides

Lvalues and Rvalues


lvalue: expression which may appear on either left-hand or right hand of assignment (=)
rvalue: expression which may appear on only the right hand of assignment (=)

Function Declarations vs Definitions


function declaration (or prototype): shows how to call the function
Return_Type Function_Name(Parameter_List); //Comment describing what
function does
function definition: describes how the function performs task

includes function header and body

Call Mechanisms
pass-by-value: can only change a copy of the input
pass-by-reference: can change the actual input

Arrays
fixed sized, indexed data type that stores items of the same type
declaration: int b[4]; //creates array "b" with up to 4 elements
accessing elements: b[i] //gives the element at index i
An array is passed by reference in a function.
*Note: array == &array[0]

Pointers
Used to store location in memory of variable, rather than value.

1 int foo = 1;
2 int *bar; //define a pointer
3 bar = &foo; //sets bar to address of foo
4 *bar = 2; //uses dereference operation, so actually changes
value of foo
Pointers offer a nice mechanism to work with arrays.

References
A reference is an alternative name for an object.

1 int iVal = 1024;


2 int &refVal = iVal; // refVal is a reference to iVal

We can now change iVal through refVal .

A reference must be initialized with a variable of the same type

1 int &refVal2; // Error: not initialized


2 int &refVal3 = 10; // Error: 10 is not
3   // a variable

There is also no way to rebind a reference to a different object. In this way, a pointer is more
flexible.

Example

Structs
Define a new type with its own unique components.

Example

1 struct Grades {
2 char name[9];
3 int midterm;
4 int final;
5 };

This defines a struct called Grades.

We can declare a new object of this type:

1 struct Grades alice = {"Alice", 60, 85};


and can change each component: alice.midterm = 65;

*Note for a pointer to a struct, access component with -> :

1 struct Grades *gPtr = &alice; //declares pointer


2 gPtr->final = 90; //changes alice.final to 90

Const
To slides

Sometimes, we want to use a numerical value multiple times in a file. Instead of using this
number multiple times throughout the file, we use a constant

1 const int MAXSIZE = 256; //declare constant


2 char name[MAXSIZE]; //use it to declar a char array

const Reference
A const reference can be initialized to an rvalue or lvalue, but a regular reference cannot.

1 const int &ref = 10; // OK


2 const int &ref = iVal+10; // OK
3
4 int &ref = 10; // ERROR
5 int &ref = iVal+10; // ERROR

A common use of const reference is to pass a struct or class as a function argument.

1 int avg_exam(const struct Grades & gr){


2 return (gr.midterm+gr.final)/2;
3 }

Why?

Pass-by-value can be expensive, for large structures


Pass-by-reference allows for the possibility of accidentally changing the structure.

const Pointers
Pointers have two changeable aspects:

1. The value of the pointer.


2. The value of the object to which the pointer points.

Both can be assigned a const qualifier.

Read these right to left (e.g. int *const p is a "constant pointer to an int") and note that const
int is equivalent to int const .
1 const int *p; // the pointed-to int cannot be changed by pointer p
2
3 int *const p; // "p" (the pointer) cannot be changed
4
5 const int *const p; // neither can be changed.

*Note that the above int can still be changed by other means, just not by the constant pointer

You can use a int * type anywhere you would expect a const int * type but not vice versa
because code that expects a const int * type is guaranteed not to try and change it.

Using typedef

We can use typedef to define custom types. For example,

1 typedef int * intptr_t;

defines a type intptr_t which is a pointer to an int type.

Question

How do we use typedef to rename the type of T *const ?

1 typedef const T const_t;


2 typedef const_t *constptrT_t;

1 typedef T *ptrT_t;
2 typedef ptrT_t const constptrT_t;

1 typedef const * constptr_t;


2 typedef constptr_t T constptrT_t;

1 typedef T * const constptrT_t;

Procedural Abstraction
To slides

Abstraction is the concept that only the details that matter are provided so that unnecessary
complexity is avoided. The important thing is knowing what it does, not how it does it.

A function is a way of providing "computational" abstractions.

the person who implements the function is called the author


the person who uses the function is called the client

Procedural abstractions have two important properties

1. Local: the implementation of an abstraction doesn't depend on any other abstraction


implementation
2. Substitutable: different implementations can be substituted without affecting how the
client uses it
We describe the abstraction of a function with specifications comment.

1 int some_fxn(){
2 //REQUIRES: what pre-conditions?
3    //MODIFIES: what is changed, and how does it change?
4    //EFFECTS: what does the procedure do?
5 ...
6 }

*Note that REQUIRES and MODIFIES are optional if there are no dependencies

Functions without a REQUIRES clause are complete, meaning they are valid for all inputs.
Otherwise, they are considered partial.

we can make partial functions complete using exceptions

Recursion
To slides

A recursive function has two important parts:

a base case, for breaking out of the loop


a condition under which the function calls itself

Example

1 int factorial (int n) {


2 // REQUIRES: n >= 0
3 // EFFECTS: computes n!
4 if (n == 0) return 1; // base case
5 else return n*factorial(n-1); // recursive step
6 }

We can also use helper functions to make the recursion easier or to change the problem slightly.
This helper function can have different argments than the main function.

Example

Consider this function and its helper function, which determine whether a string is a palindrome.

1 bool is_palindrome_helper(string s, int begin, int end)


2 // EFFECTS: returns true if the subtring
3 // of s starting at begin and ending at
4 // end is a palindrome.
5 {
6    if(begin >= end) return true;
7    if(s[begin] == s[end])
8    return is_palindrome_helper(s, begin+1, end-1);
9    else return false;
10 }
1 bool is_palindrome(string s)
2 // EFFECTS: returns true if s is
3 // a palindrome.
4 {
5 return is_palindrome_helper(s, 0,
6 s.length()-1);
7 }

Question

Which of the following is true?

Any loop can be turned into a recursive function.

Any recursive function can be turned into a loop.


A loop is generally faster than a recursive function.

A recursive function is generally faster than a loop.

Function Pointers
To slides

Function pointers are used to avoid writing many functions that have similar structures.

Consider two functions:

1 int min(int a, int b);


2 // EFFECTS: returns the smaller of a and b.
3 int max(int a, int b);
4 // EFFECTS: returns the larger of a and b.
5

They both have the same type signature, 2 int inputs and 1 int output.

We can define a variable that points to a function with this type signature:

1 int (*foo)(int, int);

foo can then be assigned to a function and be used to call it

1 foo = min;
2 foo(3,5) //this will return min(3,5)

*Note it is not necessary to write foo = &min like a regular pointer

Example

Two functions smallest and largest are defined as follows:

1 int smallest(list_t list)


2 // REQUIRES: list is not empty
3    // EFFECTS: returns smallest element
4    // in the list
5 {
6    int first = list_first(list);
7    list_t rest = list_rest(list);
8    if(list_isEmpty(rest)) return first;
9    int cand = smallest(rest);
10    if(first <= cand) return first;
11    return cand;
12 }
13
14 int largest(list_t list)
15    // REQUIRES: list is not empty
16    // EFFECTS: returns largest element
17    // in the list
18 {
19    int first = list_first(list);
20    list_t rest = list_rest(list);
21    if(list_isEmpty(rest)) return first;
22    int cand = largest(rest);
23    if(first >= cand) return first;
24    return cand;
25 }
26

Given the above functions max and min , we can rewrite them with the function compare_help
with a function pointer:

1 int compare_help(list_t list, int (*fn)(int, int))


2 {
3    int first = list_first(list);
4    list_t rest = list_rest(list);
5    if(list_isEmpty(rest)) return first;
6    int cand = compare_help(rest, fn);
7    return fn(first, cand);
8 }
9
10 int smallest(list_t list)
11 {
12 return compare_help(list, min);
13 }
14
15 int largest(list_t list)
16 {
17    return compare_help(list, max);
18 }

Call Stacks
When a function is called, several steps are performed:

1. Evaluate the arguments (e.g. if max(4-1,5) is called, 4-1 must be evaluated first)
2. Create an activation record (or stack frame) to hold the function's formal parameters
and local variables.
3. Copy actual variables' values to the formal parameters' storage space.
4. Evaluate the function in its scope.
5. Replace the function call with the result.
6. Destroy the activation record.
When functions call other functions, the activation records are added to the stack. These records
enter and exit the stack on a last in, first out basis. A function call cannot resolve unless the
functions it calls resolve first.

Enumeration
To slides

Enumerations are useful for categorizing data. For example instead of encoding the four card
suits as

1 const int CLUBS = 0;


2 const int DIAMONDS = 1;
3 // and so on…

We can define an enum type that contains this information.

1 enum Suit_t {CLUBS, DIAMONDS, HEARTS, SPADES};

A variable of this type can be defined with

1 enum Suit_t suit = DIAMONDS;

Program Arguments
To slides

I/O
To slides

Question
input: “VE 280 is the best course ever!” What are the contents of the variables?

1 int x;
2 string s1, s2;
3 cin >> s1 >> x >> s2;

A. s1 contains “VE”.

B. x contains 280.

C. s2 contains “is the best course ever!”.

D. s2 contains “is the best course ever”.

Notice that there is a space after the word "is" so s2 should simply contain the string "is."

Redirection - ??
Several redirection operators:
">" - redirects output to a file
"2>" - redirects to error output file
"2>>" - appends output to file

1 ./a.out 1 a 2> error.log > output.txt

...

Failed Input Streams


Say you have this code:

1 int foo;
2 cin >> foo;

And the user enters "abc".

Then the stream will enter a failed state.

You can test the state of the input stream, which is a boolean, with:

1 if(cin) {...}
2 //or
3 while(cin) {...}

You can reset the state of the file stream:

1 //TODO

File Streams
How to create file streams:

1 #include <fstream>
2
3 //creates input file stream object
4 ifstream iFile;
5
6 //creates output file stream object
7 ofstream oFile;

Use the extraction operator (>>) or getline() to read information:

1 int bar;
2 iFile >> bar;
3 string baz;
4 getline(iFile, baz)

Similarly, to write to a file, use (<<):


1 string foo;
2 oFile << foo;

To open a file:

1 iFile.open("name_of_file")

After using a file, you should close it!

reduces chance of data corruption if abnormal exit


need to close an output file before reading data from it
however, the program will close all files if it ends normally

1 file_stream.close();

Failed File Streams


A file stream enters a failed state if:

it cannot be opened
you attempt to read past the end of the file

Test state similarly to input stream:

1 if(iFile) {...}

Example of file reading

1 while(iFile) {
2 getline(iFile, line);
3 cout << line << endl;
4 }

There is a problem with this!

The program still thinks that the file stream is good after receiving the last line so getline() will be
called an extra time and will read nothing. This means an extra unwanted line will be printed.

We fix this be checking if the file stream is good before printing:

1 while(iFile) {
2 getline(iFile, line);
3    if(iFile){
4       cout << line << endl;
5   }
6 }

Or even better (and more correct):

1 while(getline(iFile, line)){
2    cout << line << endl;
3 }
String Streams
Say you've read a line into a variable "line."

There is a int and a double contained in the string that you want to read into separate variables.

We can use an input string stream to read these characters into their proper types.

1 #include <sstream>
2
3 //open input string stream
4 istringstream iStream;
5
6 //open output string stream
7 ostringstream oStream;

We assign the stream to a string:

1 iStream.str(some_string);

Using the extraction operator:

1 istringstream iStream;
2 int foo;
3 double bar;
4 iStream.str(line);
5 iStream >> foo >> bar;

Similarly, there is an output string stream:

1 int foo = 512;


2 int bar = 1024;
3 string result;
4 ostringstream oStream;
5 oStream << foo << " " << bar;
6 result = oStream.str()

Here, result = "512 1024"

Fixed Field Width

1 #include <iomanip>
2
3 cout << foo << setw(4) << bar << endl;

This sets the width of the following number, right-aligns it, and pads with empty space.

Testing
To slides
Debugging Testing

Debugging - fixing the error in the code


Testing - finding the error in the code

* Always assume there is an error in the code!

Thinking that your code is perfect is an essential nature of the mistake itself.

Testing only when program is "finished" leads to larger workload due to small pervasive errors.

Instead, practice incremental testing - testing pieces of the program as you write them.

leads to smaller tests, easier to understand


code will be fresh in your mind

Exceptions
To slides

Basic Structure
1. Try -> block of code
2. Catch -> Handle the error (e.g. throw an error code)

Example Exception in C++


Factorial check for negative inputs:

1 int factorial(int n){


2 //EFFECTS: returns n! if n>=0
3    //   throws n otherwise
4    int result;
5    if (n<0) throw n;
6    for(result = 1; n!=0; n--){
7        result *= n;
8   }
9    return result;
10 }

Now we can call factorial() inside a try block, with a catch block to handle the error:
1 int foo(int i){
2    try {
3        cout << factorial(i) << endl;
4   } catch (int v) {
5        cout << "Error: negative input: ";
6        cout << v << endl;
7   }
8 }

* It is important to note that the type of catch (line 4) must match the type thrown by the
function.

You can add as many catch blocks as you'd like.

Add a final catch block to as a "catch-all"

1 catch (...) { }

Abstract Data Types


To slides

Abstract Data Types (ADTs) are similar to functions in that you don't know how the information is
represented; you only know what operations can be performed on it.

Allows for information to be hidden about the object


allows for encapsulation, where objects and their operations are defined in the same place.

Benefits:

ADTs are local: the implementation of other components does not depend on the
implementation of the ADTs
ADTs are substitutable: you can change the implementation of the ADTs without affecting
the program.

Only the operations that define the type should have access to the ADT representation

C++ Classes
An Example

1 class anInt {
2    // OVERVIEW: a trivial class to get/set a
3 // single integer value
4    int v;
5 public:
6    int get_value();
7    // EFFECTS: returns the current
8 // value
9    void set_value(int newValue);
10    // MODIFIES: this
11 // EFFECTS: sets the current value
12 // equal to newValue
13 }

 
Everything after "public: " can be used by the customer, however you can still declare hidden
helper functions before

Constructors

Constructors are used to solve the fact that a class's data members are uninitialized.

It has the following signature:

1 class IntSet {
2   ...//OVERVIEW would go here
3 public:
4    IntSet();
5    // EFFECTS: creates an empty IntSet
6 }

The name of the function is the same as the name of the class.
This function doesn't have a return type.
It also does not take an argument in this case.
It is guaranteed to be the first function called immediately after an object is created.
It builds a “blank” uninitialized IntSet and makes it satisfy the rep invariant

Initialization syntax:

1 // this is in .h file
2 class Class_T {
3    int anInt;
4    double aDouble;
5    string aString;
6 public:
7 Class_T();
8 }

1 // in .cpp file
2 Class_T::Class_T(): anInt(0),
3 aDouble(1.2),
4    aString("Yes")
5   {
6        
7   }

Const Member Functions

A slight change to the class definition:


1 const int MAXELTS = 100;
2 class IntSet {
3    int elts[MAXELTS];
4    int numElts;
5    int indexOf(int v) const;
6 public:
7    void insert(int v);
8    void remove(int v);
9    bool query(int v) const;
10    int size() const;
11 };

*Notice the const at the end of some lines

int size() const ;

Each member function of a class has an extra, implicit parameter named this .

this is a pointer to the current instance on which the function is invoked

const modifies the implicit this pointer: this is now a pointer to a const instance.

this means the member function size() cannot change the object on which size() is
called ( size() shouldn't be able to anyway by definition!)

For example

1 int IntSet::size() const {


2 return numElts;
3 }

a const object can only call its const member functions!

1 const IntSet is;


2 cout << is.size(); // allowed
3 is.insert(2); // NOT allowed

a const member function can only call other const member functions as well!

1 //if
2 void A::g() const { f(); }
3
4 //then
5 void A::f() const {...} //Allowed
6 void A::f() {...} //NOT allowed
7

Improving Efficiency

The time to check for if an element is in an unsorted set grows linearly with the size of the set.

For large sets, this becomes too expensive.

Luckily, we can replace the implementation with something more efficient by using a sorted
order.

Consider:
1 const int MAXELTS = 100;
2    class IntSet {
3    // OVERVIEW: a mutable set of integers
4    int elts[MAXELTS];
5    int numElts;
6    int indexOf(int v) const;
7 public:
8    IntSet();
9    void insert(int v);
10    void remove(int v);
11    bool query(int v) const;
12    int size() const;
13 };

What should be changed?

remove()

instead of moving the last element to fill the hole, we will shift everything down one to
preserve order

1 void IntSet::remove(int v) {
2    int victim = indexOf(v);
3        if (victim != MAXELTS) {
4       // victim points at hole
5        numElts--; // one less element
6        while (victim < numElts) {
7       // ..hole still in the array
8       elts[victim] = elts[victim+1];
9       victim++;
10       }
11   }
12 }
13

insert()

Instead of inserting at the end of the array, we check starting at the beginning of the list
and insert the element in the appropriate spot.

1 void IntSet::insert(int v) {
2    if (indexOf(v) == MAXELTS) { // duplicate not found
3        if (numElts == MAXELTS) throw MAXELTS; // no room
4       int cand = numElts-1; // last element
5        while ((cand >= 0) && elts[cand] > v) {
6            elts[cand+1] = elts[cand];
7            cand--;
8       }
9        // Now, cand points to the left of the "gap".
10        elts[cand+1] = v;
11        numElts++; // repair invariant
12   }
13 }

*Note on line 5 we use "short-circuit" property of && , meaning the right side is not evaluated
unless the left side is true.
this means the function will never try to call an element at a negative index

We can also increase the efficiency of indexOf() by implementing binary search.

Question
How many elements of the array must indexOf() examine?

1 int IntSet::indexOf(int v) {
2 for (int i = 0; i < numElts; i++) {
3 if (elts[i] == v) return i;
4 }
5 return MAXELTS;
6 }

A. In the best case, 1 element

B. In the worst case, numElts elements

C. In the worst case, MAXELTS elements

D. None of the above

Subtypes and Inheritance


...

Question

Select all correct answers:

A. The type int, is not a subtype of double, but it is a subtype of long.

B. The type string is a supertype of the type representing a C string.

C. If C is a subtype of B and B is a subtype of A, then an instance of C can substitute to an


instance of A.

D. If C is a supertype of B and B is a supertype of A, then an instance of C can substitute to


an instance of A.

*Note

A is wrong because there would be a conversion from int to long


B is wrong because there is no relation because a string type and a C string type

...
Question

Select all correct answers:

A. An ADT can be implemented as a class in C++.

B. A class in C++ is always an ADT.

C. A subtype can be implemented as a subclass in C++.

D. A subclass in C++ is always an ADT subtype.

*Note

B is wrong because you can declare a class in many ways (e.g. all public members)

...

Interfaces and Invariants


To slides

...

Dynamic Memory Allocation and Destructors


To slides

...

Deep Copy
To slides

...

Dynamic Resizing
To slides

Linked Lists
To slides

Question

What would be the impact of using Linked List instead of an array for defining IntSet?

A. It is not possible to redefine IntSet with Linked List.


B. Some operations (among query, insert, remove) of IntSet would be faster if we were to
use Linked List instead of an array.

C. Some operations (among query, insert, remove) of IntSet would be slower if we were to
use Linked List instead of an array.

D. IntSet would be more memory efficient.

Question

Do you agree that the copy operation can be performed as follows?

1 IntList::IntList(const IntList &l): first (0){


2 if (!l.first) return;
3 node *current = l.first;
4 while(current){
5 this->insert(current->value);
6 current = current->next;
7 }
8 }

Select all the correct answers.

A. Yes, all the values are copied.

B. No, some values are not copied.

C. No, even if all the values are copied.

D. In fact, sometimes it works. (*Note only works with one element)

Question

Which of the following statements are true when comparing doubly-linked lists and singly-linked
lists?

A. Doubly-linked lists allows more operations.

B. Doubly-linked lists make some operations more efficient.

C. Doubly-linked lists make some operations less efficient.

(For instance, insert requires updating 2 pointers)

D. Doubly-linked lists double the memory requirement compared to singly-linked list.

Templates and Containers


To slides

Containers or Container classes are simply classes that are used to store things, and generally
have no intrinsic meaning on their own.

A container class that contains int types will have the same structure as one that contains char
types. Consider:
1 struct node {
2 node *next;
3 int v;
4 };
5 class List {
6 node *first;
7 public:
8 void insert(int v);
9 int remove();
10 ...
11 };

If we replaced every int with char we would define a list-of-char.

We avoid reusing code for these two containers by using a template.

Example List Template

1 template <class T>


2 class List {
3    public:
4        bool isEmpty();
5        void insert(T v);
6        T remove();
7    
8        List();
9        List(const List &l);
10        List &operator=(const List &l);
11        ~List();
12    
13    private:
14   ...
15 };

Notice that we have a type T as a place holder.

We must also pick a representation for the node contained in the List. We do this by creating a
private type, meaning it is only available to implementations of this class's methods.

1    private:
2        struct node{
3            node *next;
4            T v;
5       };

The rest of the private definition is as expected.

1 node *first;
2 void removeAll();
3 void copyList (node* np);

Then all that is left is to define the method bodies. Each method must be declared as a
"templated" method, with the List<T> namespace.
1    template <class T>
2        void List<T>::insert(T v) {
3        node *np = new node;
4        np->next = first;
5        np->v = v;
6        first = np;
7   }

When using template, class member functions should be defined in the .h file, following the
class definition. so there is no associated .cpp file.

Pay attention to the <T> placement!

1 //Constructor
2 List<T>::List()
3 List<T>::List(const List<T> &l)
4
5 //Destructor
6 List<T>::~List()
7
8 //Assignment Operator
9 List<T> &List<T>::operator=(const List<T> &l)

To use a template:

1 // Create a static list of integers


2 List<int> li;
3
4 // Create a dynamic list of integers
5 List<int> *lip = new List<int>;
6
7 // Create a dynamic list of doubles.
8 List<double> *ldp = new List<double>;

Last Question

ANS: A,B,C,D

Operator Overloading
To slides

Linear Lists and Stack


To slides

Queues
To slides
Standard Template Library: Sequential Containers
To slides

Standard Template Library: Container Adaptors and


Associative Containers
To slides

Example Initialization

1 template <class T, class C=deque<T> >;


2 class stack {...}

Question

To build a queue, we can use:

Vector (no, can only add in the back)

List

Deque

Stack

*Note: a queue should have operations on both sides

Example

1 stack<int> stk;
2 for(int i=0; i<5; i++)
3 stk.push(i);
4 while(!stk.empty()) {
5 cout << stk.top() << endl;
6 stk.pop();
7 }
8

Output:

1 4
2 3
3 2
4 1
5 0

Dictionary
Is essentially a collection of keys and values

*Note: keys will be unique in a dictionary

Dictionaries are optimized to quickly add a (key, value) pair and retrieve values by their key.

Methods
Value find(Key k) - returns value whose key is k
void insert(Key, Value v) - inserts pair (k, v) into the dictionary. If the pair with key
k already exists, update its value.
Value remove (Key k) - Remove the pair with key as k from the dictionary and return its
value. Return Null if none.
int size() - return number of pairs in the dictionary.

Implementation

1. Use an array
2. Use a linked list

To the top

Das könnte Ihnen auch gefallen