Beruflich Dokumente
Kultur Dokumente
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
Follows format:
permissions # of links owner group file size last modification file name
rm - removes files
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> .
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.
*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.
1 Target: Dependency
2 Command
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
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.
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 };
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
const Reference
A const reference can be initialized to an rvalue or lvalue, but a regular reference cannot.
Why?
const Pointers
Pointers have two changeable aspects:
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
Question
1 typedef T *ptrT_t;
2 typedef ptrT_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.
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.
Recursion
To slides
Example
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.
Question
Function Pointers
To slides
Function pointers are used to avoid writing many functions that have similar structures.
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 foo = min;
2 foo(3,5) //this will return min(3,5)
Example
Given the above functions max and min , we can rewrite them with the function compare_help
with a function pointer:
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
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.
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 int foo;
2 cin >> foo;
You can test the state of the input stream, which is a boolean, with:
1 if(cin) {...}
2 //or
3 while(cin) {...}
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;
1 int bar;
2 iFile >> bar;
3 string baz;
4 getline(iFile, baz)
To open a file:
1 iFile.open("name_of_file")
1 file_stream.close();
it cannot be opened
you attempt to read past the end of the file
1 if(iFile) {...}
1 while(iFile) {
2 getline(iFile, line);
3 cout << line << endl;
4 }
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.
1 while(iFile) {
2 getline(iFile, line);
3 if(iFile){
4 cout << line << endl;
5 }
6 }
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;
1 iStream.str(some_string);
1 istringstream iStream;
2 int foo;
3 double bar;
4 iStream.str(line);
5 iStream >> foo >> bar;
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
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.
Exceptions
To slides
Basic Structure
1. Try -> block of code
2. Catch -> Handle the error (e.g. throw an error code)
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.
1 catch (...) { }
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.
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.
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 }
Each member function of a class has an extra, implicit parameter named this .
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
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.
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 };
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
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 }
Question
*Note
...
Question
*Note
B is wrong because you can declare a class in many ways (e.g. all public members)
...
...
...
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?
C. Some operations (among query, insert, remove) of IntSet would be slower if we were to
use Linked List instead of an array.
Question
Question
Which of the following statements are true when comparing doubly-linked lists and singly-linked
lists?
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 };
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 };
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.
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:
Last Question
ANS: A,B,C,D
Operator Overloading
To slides
Queues
To slides
Standard Template Library: Sequential Containers
To slides
Example Initialization
Question
List
Deque
Stack
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
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