CSE332S Object-Oriented Programming in C++ (Lecture 4)
Namespace details
Motivation
Classes encapsulate behavior (methods) and state (member data) behind an interface.
Structs are similar, but with state accessible.
Classes and structs are used to specify self contained, cohesive abstractions.
- Can say what class/struct does in one sentence.
What if we want to describe more loosely related collections of state and behavior?
Could use a class or struct
- But that dilutes their design intent.
Namespace
Cpp offers an appropriate scoping mechanism for loosely related aggregates: Namespaces.
- Good for large function collections.
- E.g. a set of related algorithms and function objects
- Good for general purpose collections
- E.g. program utilities, performance statistics, etc.
Declarative region
- Where a variable/function can be used
- From where declared to end of declarative region
Namespace Properties
Declared/(re)opend with namespace keyword.
namespace name { ... }namespace name = namespace existing_name { ... };
Access members using scoping operator::
std::cout << "Hello, World!" << std::endl;
Everything not declared in another namespace is in the global namespace.
Can nest namespace declarations
namespace outer { namespace inner { ... } }
Using Namespaces
The using keyword make elements visible.
- Only apples to the current scope.
Can add entire name space to the current scope
using namespace std;cout << "Hello, World!" << endl;
Can also declare unnamed namespaces
- Elements are visible after the declaration
namespace { int i = 42; }will makeivisible in the current file.
C-style vs. C++ strings
C++ string class
#include <iostream>
#include <string>
using namespace std;
int main (int argc, char *argv[]) {
string s = "Hello,";
s += " World!";
cout << s << endl; // prints "Hello, World!"
return 0;
}- Using
<string>header - Various constructions
- Assignment operator
- Overloaded operators
- Indexing operator, we can index cpp strings like arrays,
s[i]
C-style strings
#include <iostream>
#include <cstring>
using namespace std;
int main (int argc, char *argv[]) {
char *h = "Hello, ";
string sh = "Hello, ";
char *w = "World!";
string sw = "World!";
cout << (h < w) << endl; // this returns 0 because we are comparing pointers
cout << (sh < sw) << endl; // this returns 1 because we are comparing values of strings in alphabetical order
h += w; // this operation is illegal because we are trying to add a pointer to a pointer
sh += sw; // concatenates the strings
cout << h << endl; // this prints char repeatedly till the termination char
cout << sh << endl; // this prints the string
return 0;
}- C-style strings continguous arrays of char
- Often accessed as
char *by pointer.
- Often accessed as
- Cpp string class provides a rich set of operations.
- Cpp strings do “what you expected” as a programmer.
- C-style strings do “what you expected” as a machine designer.
Use cpp strings for most string operations.
Cpp native array
Storing Other Data Types Besides char
There are many options to store non-char data in an array.
Native C-style arrays
- Cannot add or remove positions
- Can index positions directly (constant time)
- Not necessary zero-terminated (no null terminator as ending)
STL list container (bi-linked list)
- Add/remove position on either end
- Cannot index positions directly
STL vector container (“back stack”)
- Can add/remove position at the back
- Can index positions directly
Pointer and Arrays
#include <iostream>
using namespace std;
int main (int argc, char *argv[]) {
int a[10];
int *p = &a[0];
int *q = a;
// p and q are pointing to the same location
++q; // q is now pointing to the second element of the array
}An array holds a contiguous sequence of memory locations
- Can refer to locations using either array index or pointer location
int a[0]vsint *pa[i]vs*(a + i)
Array variable essentially behaves like a const pointer
- Like
int * const arr; - Cannot change where it points
- Can change locations unless declared as const, eg
const int arr[10];
Can initalize other pointers to the start of the array
- Using array name
int *p = a;int *p = &a[0];
Adding or subtracting int pointer n moves a pointer by n of the type it points to
int *p = a;p += 1;moves pointer by 1sizeof(int)p -= 1;moves pointer by 1sizeof(int)
Remember that cpp only guarantees sizeof(char) is 1.
Array of (and Pointers to) Pointers
#include <iostream>
using namespace std;
int main (int argc, char *argv[]) {
// could declare char ** argv
for (int i = 0; i < argc; i++) {
cout << argv[i] << endl;
}
return 0;
}Can have array of pointers to pointers
Can also have an array of pointers to arrays
int (*a)[10];a[0]is an array of 10 intsa[0][0]is the first int in the first array
Rules for pointer arithmetic
#include <iostream>
using namespace std;
int main (int argc, char *argv[]) {
int a[10];
int *p = &a[0];
int *q = p + 1;
return 0;
}You can subtract pointers to get the number of elements between them (no addition, multiplication, or division)
int n = q - p;nis the number of elements betweenpandq
You can add/subtract an integer to a pointer to get a new pointer
int *p2 = p + 1;p2is a pointer to the second element of the arrayp+(q-p)/2is allowed but not(p+q)/2
Array and pointer arithmetic: Given a pointer p and integer n, p[n] is equivalent to *(p+n).
Dereferencing a 0 pointer is undefined behavior.
Accessing memory outside of an array may
- Crash the program
- Let you read/write memory you shouldn’t (hard to debug)
Watch out for:
- Uninitialized pointers
- Failing to check for null pointers
- Accessing memory outside of an array
- Error in loop initialization, termination, or increment
Dynamic Memory Allocation
Aray can be allocated, and deallocated dynamically
Arrays have particular syntax for dynamic allocation
Don’t leak, destroy safely.
Foo * baz (){
// note the array form of new
int * const a = new int[3];
a[0] = 1; a[1] = 2; a[2] = 3;
Foo *f = new Foo;
f->reset(a);
return f;
}
void Foo::reset(int *a) {
// ctor must initialize to 0
delete [] this->array_ptr;
this->array_ptr = a;
}
void Foo::~Foo() {
// note the array form of delete
delete [] this->array_ptr;
}Vectors
#include <iostream>
#include <vector>
using namespace std;
int main (int argc, char *argv[]) {
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
// note that size_t is an unsigned type that is guaranteed to be large enough to hold the size of v.size(), determined by the compiler.
for (size_t i = 0; i < v.size(); i++) {
cout << v[i] << endl;
}
// this will print 1, 2, 3
return 0;
}Motivation to use vectors
Vector do a lot of (often tricky) dynamic memory management.
- use new[] and delete[] internally
- resize, don’t leak memory
Easier to pass to functions
- can tell you their size by
size() - Don’t have to pass a separate size argument
- Don’t need a pointer by reference in order to resize
Still have to pay attention
push_backallocates more memory but[]does not- vectors copy and take ownership of elements
IO classes
#include <iostream>
using namespace std;
int main (int argc, char *argv[]) {
int i;
// cout == std::ostream
cout << "Enter an integer: ";
// cin == std::istream
cin >> i;
cout << "You entered: " << i << endl;
return 0;
}<iostream> provides classes for input and output.
- Use
<istream>for input - Use
<ostream>for output
Overloaded operators
<<for insertion>>for extraction (terminates on whitespace)
Other methods
ostreamwriteput
istreamgeteofgoodclear
Stream manipulators
ostream:flush,endl,setwidth,setprecision,hex,boolalpha(boolalpha is a manipulator that changes the way bools are printed from 0/1 to true/false).
File I/O
#include <iostream>
#include <fstream>
using namespace std;
int main (int argc, char *argv[]) {
ifstream ifs;
ifs.open("input.txt", ios::in);
ofstream ofs ("output.txt", ios::out);
if (!ifs.is_open() && ofs.is_open()) {
int i;
ifs >> i;
ofs << i;
}
ifs.close();
ofs.close();
return 0;
}<fstream> provides classes for file input and output.
- Use
<ifstream>for input - Use
<ofstream>for output
Other methods
opencloseis_opengetlineparses a line from the file, defaults to whitespaceseekgseekp
File modes:
inlet you read from the fileoutlet you write to the fileatelet you write to the end of the fileapplet you write to the end of the filetrunclet you truncate the filebinarylet you read/write binary data
String Streams Classes
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
int main (int argc, char *argv[]) {
ifstream ifs("input.txt", ios::in);
if (!ifs.is_open()) {
string line_1, word_1;
getline(ifs, line_1);
istringstream iss(line_1);
iss >> word_1;
cout << word_1 << endl;
}
ifs.close();
return 0;
}<sstream> provides classes for string streams.
- Use
<istringstream>for input - Use
<ostringstream>for output
Useful for scanning input
- Get a line form file into a string
- Wrap a string into a stream
- Pull words off the stream
#include <iostream>
#include <sstream>
using namespace std;
int main (int argc, char *argv[]) {
if (argc < 3) return 1;
ostringstream argsout;
argsout << argv[1] << " " << argv[2] << endl;
istringstream argsin(argsout.str());
float f,g;
argsin >> f;
argsin >> g;
cout << f << "/" << g << "is" << f/g << endl;
return 0;
}Useful for formatting output
- Using string as format buffer
- Wrapping a string into a stream
- Push formatted values into the stream
- Output the stream to file
Program gets arguments as C-style strings
Formatting is tedious and error prone in C-style strings (sprintf, etc.)
iostream formatting is friendly.