CS 1666 www.cs.pitt.edu/~nlf4/cs1666/ Programming in C++
First, some praise for C++ "It certainly has its good points. But by and large I think it s a bad language. It does a lot of things half well and it s just a garbage heap of ideas that are mutually exclusive." Ken Thompson, designer and implementer of Unix "It would make him physically ill to think of programming in C++" Donald Knuth, referring to Edsgar Dijkstra "Within C++, there is a much smaller and cleaner language struggling to get out" Bjarne Stroustrup Creator of C++ 2
Goals for C++ Be a better C Continue to provide low-level performance control Stronger typechecking Not entirely backwards compatible "As Close as Possible to C, but no Closer" Support data abstraction Support object-oriented programming Support generic programming 3
C Family Tree by Bjarne Stroustrup 4
C++ history Initial work on "C with classes" started in 1979 Precursor to C++ Renamed to C++ in 1983 C++ 2.0 released in 1989 First C++ standard published by ISO in 1998 C++98 More recent standards: C++03 C++11 C++14 C++17 5
Hello world #include <iostream> int main() { } std::cout << "Hello, World!\n"; 6
The scope resolution operator :: Has a few use cases, e.g.: Accessing global variables int x = 0; int main() { int x = 0; x = 1; ::x = 2; } 7
Other scope resolution operator uses To define class methods outside of the class To access static class attributes To sort out ambiguities in multiple inheritance Accessing members of scoped enums To access identifiers defined within a namespace 8
Namespaces namespace A { int x = 7; } namespace B { int x = 8; } int x = 0; int main() { int x = 0; x = 1; ::x = 2; A::x = 3; B::x = 4; } 9
Hello world (again) #include <iostream> int main() { using namespace std; cout << "Hello, World!\n"; } 10
Streams Streams convert typed objects to/from a stream of characters (bytes) std::cout is the standard output stream (STDOUT) << is the output (or "put to") operator Overloaded to operate on a stream and different types Returns a reference to the stream that was output to cout << 1 << "b" << var5; 11
Input streams cin is the standard input stream (STDIN) >> is the input (or "get from") operator Again returns a reference to the original stream to allow for chaining Type of right hand side determines type of value being read Generally considers and whitespace to be a delimiter To read a line with whitespace, use std::getline() Will stop reading a number when first non-numeric character is encountered. 12
Hello world (one more time) #include <iostream> using std::cout; using std::endl; int main() { } cout << "Hello, World!" << endl; 13
Variable declarations Most of the C types you know and love are available: E.g., char, bool, short, int, long, signed, unsigned, float, double, void, etc. Has additional syntax for initialization via initializer lists double d1 = 0.1; double d2 {2.3}; int i1 = 4.5; int i2 {6.7}; Can use auto type when type is clear from initialization: auto d3 = 8.9; auto i3 {10}; 14
Operators + - * / % ++ -- += -= *= /= %= ==!= < <= > >= &&! & ^ ~ 15
Scope Local Global Class Namespace 16
Constants const "I promise not to change this value" const int ans = 42; constexpr Must be evaluated at compile time constexpr int ans = 42; constexpr int res = sum(x, y); Error, cannot be evaluated at compile time 17
Pointer review int x; int* y; int& z; Error int& z = x; &x *y 18
Range for statement In addition to C-style for loops, C++ offers a range for statement for (auto x : arr) cout << x << endl; Copies each item in array arr into x and executes loop body for (auto& x : arr) cout << x << endl; Creates a reference to each array element and executes loop body 19
NULL What is the difference between NULL, 0, and '\0'? C++ improves these easily confused points with the introduction of nullptr nullptr is a standin for the null pointer There is only one, shared by all pointer types Cannot be converted to an int Can be converted to a bool Given int* p: if(*p) is the same as if(*p!= 0) if(p) is the same as if(p!= nullptr) 20
OOP class Rectangle { public: Rectangle(int l, int w) :length{l}, width{w} {} void printarea() { std::cout << area() << std::endl; } private: int length, width; int area() { return length * width; } }; 21
OOP2 class Square { public: Square(int s); void printarea(); private: int side; int area(); }; Square::Square(int s) { side = s; } void Square::printArea() { std::cout << area() << std::endl; } int Square::area() { return side * side; } 22
Inline functions Compiler will try to generate code at each point of call instead of using function call instruction Can save on function call overhead! Use the inline keyword before return type in function declaration Or define a method within the class definition E.g., like we did in Rectangle Can still use inline keyword on methods not defined in class definition Note that compiler may not be able to abide by the request to inline 23
-> vs. Rectangle r(5, 2); Square* s = new Square(4); r.printarea(); s->printarea(); 24
Consider the following: class MyArray { public: MyArray(int s) :arr{new int[s]}, sz{s} {} int& operator[](int i){ return arr[i]; } int size() { return sz; } private: int* arr; int sz; }; 25
Things to add A default constructor MyArray() :arr{nullptr}, sz{0} {} Note this should be invoked as MyArray a; What's wrong with MyArray a();? A destructor ~MyArray() { delete[] arr; } Cleans up memory allocated via new in constructor 26
Copying classes Consider the following: MyArray m(10); for (auto i=0; i<10; i++) { m[i] = i; } MyArray n = m; MyArray o; o = m; n.sz = 20; n[2] = 20; o.sz = 30; o[3] = 30; 27
Rule of 5 If your class needs any one of these, it probably needs all 5: Destructor Copy constructor Move constructor Copy assignment operator Move assignment operator 28
Copy constructor MyArray(const MyArray& a) :arr{new int[a.sz]}, sz{a.sz} { for (int i=0; i<sz; i++) arr[i] = a.arr[i]; } 29
Copy assignment operator MyArray& operator=(const MyArray& a) { int* p = new int[a.sz]; for (int i=0; i<a.sz; i++) p[i] = a.arr[i]; delete[] arr; arr = p; sz = a.sz; return *this; } 30
rvalues and lvalues type&& creates an rvalue reference E.g.: int x = 10; int& r = x; OK int&& rr = x; Error, x is an lvalue int& r2 = x + 5; Error, x + 5 is an rvalue int&& rr2 = x + 5; OK 31
Consider the following: A class Test that overloads operator+() Assume we have valid x and y Test objects Test t; t = x + y; We create new Test object as the result of x + y But it's an rvalue Assignment operator will perform a copy of x + y object And then the rvalue x + y object will be destroyed! Better to move instead of copy and destroy 32
Move constructor MyArray(MyArray&& a) :arr{a.arr}, sz{a.sz} { a.arr = nullptr; a.sz = 0; } 33
Move assignment operator MyArray& operator=(myarray&& a) { if (this!= &a) { arr = a.arr; sz = a.sz; a.arr = nullptr; a.sz = 0; } return *this; } 34
Templates Should be familiar from Java: template <typename T> int compare(const T& v1, const T& v2) { if (v1 < v2) return -1; if (v2 < v1) return 1; return 0; } 35
Class template template<typename T> class MyArray { private: T* arr; int sz; public: // T& operator[](int i); // }; 36
Virtual functions Functions that are declared, but not defined Prefix the function declaration with virtual Pure virtual must be implemented by a derived class Suffix the function declaration with =0 Any class with unimplemented pure virtual functions is considered an abstract class and cannot be instantiated. Abstract classes can be used to create interfaces No interface keyword like in Java Just a class with unimplemented pure virtual functions Can implement multiple interfaces due to support for multiple inheritance 37
An abstract container class class Container { public: virtual int& operator[](int) = 0; virtual int size() = 0; virtual ~Container() {} }; 38
Inheritance class MyArr_container : public Container { public: MyArr_container(int s) : m(s) { } ~MyArr_container() {} int& operator[](int i) override { return m[i]; } int size() override { return m.size(); } private: MyArray m; }; 39
Lambda expressions Anonymous functions E.g., [] (int x) { return x * x; } If no arguments passed, in argument list can be omitted Hence minimal lambda expression is [] { } Return type inferred from simple lambda expressions void if no return statement If body is only a single return statement, return type is the type of that return's expression Otherwise, return type must be specified [] (bool x) { if x return 1; else return 2; } Error, too complicated [] (bool x)->int { if x return 1; else return 2; } 40
Capture What if you want to reference a variable from the containing scope within a lambda expression? [] allows you to express how such values can be captured [] No local names used in lambda body [&] All local names available by reference [=] All local names available by value [x,&y,&z] x available by value, y and z available by reference [&,x] All available by reference except for x available by value [=,&y,&z] All available by value except for y and z available by reference 41
std::string #include <string> Generally what you should use to represent strings in C++ over char*'s you're used to from C 42
std::vector #include <vector> Unless you have a good and specific reason for using an array, you'll probably want to use a vector as your general container type in C++ 43
Iterators An iterator points to some element in a range of elements and cat to iterate through the elements of that range Implements at least: Increment operator ++ Dereference operator * std::vector::begin() returns an iterator pointing to the first element in the vector std::vector::end() returns an iterator pointing to the last element in the vector 44
Not covered (but good to know) =default and =delete member functions const member functions std::unique_ptr and std::shared_ptr std::regex Variadic Templates Compile-Time if and much more 45