Contents
Overview
* Latest standard: C++14
* Compile command with Visual Studion command line
cl /EHsc test.cpp
* Comments (same as Java and C#):
// this is a comment /* this is another comment */
* Preprocessing directives and header files
– Header files: contains definitions such as variable and function definitions
– May not contain implementations,
– So that source files can be compiled without implementation
#include <iostream>
* Functions
int main() { int answer {42}; std::cout << "Hello world! The answer is " << answer << std::endl; return 0; }
* Templates
– Class templates: used by compiler to generate classes
– Function templates: used by compiler to generate functions
* Program files
– Source files: .cpp
– Header files: .h or .hpp
– Other files, e.g. resource files
* Standard Library
* Standard template library (STL): is a subset of standard library
* How an executable file is created:
– source fiels > compile into > object files > linked into > executable file
Namespaces
* Only function prototype are required to be in a namespace
// Define namespace namespace my_ns { // function prototypes } // import namespace using namespace my_ns; using namespace std; using std::cout;
* Unnamed namespace:
– everything that’s defined can NOT be accessed outside the unnamed namespace
namespace { // function prototypes } * Nested namespaces <pre lang="cpp"> outer::inner::normalize(data);
* Namespace alias
namespace alias_name = original_namespace_name; // for example: namespace ublas = boost::numeric::ublas; ublas::vector<double> v;
Data Types
Integer
* int
int apple_count; int apple_count {15}; int foot_count {2}, toe_count {10}, head_count {1};
* Signed Integer Types
signed char: 1 byte
short
short int: 2 bytes
int: 4 bytes
long
long int: 4 bytes
long long
long long int: 8 bytes
signed char ch {20}; long temperature {-50L}; long width {500L}; long long height {250LL}; unsigned int toe_count {10U}; unsigned long angel_count {1000000UL};
Constants
const unsigned int toe_count {2U};
Integer Operators
+ – * / %
op=, e.g. += -= *= /=
++var var++ –var var–
sizeof: bytes occupied by a type
Explicit Type Casting
static_cast
double value1 {10.5}; double value2 {15.5}; int whole_number {static_cast<int>(value1) + static_cast<int>(value2)};
Floating-Point
float: pricision 7
double: pricision 15
long double: : pricision 19
lvalue and rvalue
Bitwise operators
~ & ^ |
Enumerated Data Types
#include <iostream> #include <iomanip> using std::setw; int main() { enum class Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } yesterday{ Day::Monday }, today{ Day::Tuesday }, tomorrow{ Day::Wednesday }; Day poets_day{ Day::Friday }; enum class Punctuation : char { Comma = ',', Exclamation = '!', Question = '?' }; Punctuation ch{ Punctuation::Comma }; std::cout << "yesterday's value is " << static_cast<int>(yesterday) << static_cast<char>(ch) << " but poets_day's is " << static_cast<int>(poets_day) << static_cast<char>(Punctuation::Exclamation) << std::endl; today = Day::Thursday; // Assign a new ... ch = Punctuation::Question; // ... enumerator values tomorrow = poets_day; // Copy enumerator value std::cout << "Is today's value(" << static_cast<int>(today) << ") the same as poets_day(" << static_cast<int>(poets_day) << ")" << static_cast<char>(ch) << std::endl; // ch = tomorrow; // Uncomment ... // tomorrow = Friday; // ... any of these ... // today = 6; // ... for an error. }
Synonyms for data types: typedef
typedef long BigOnes; // Defines BigOnes as a type alias BigOnes mynum {}; // Define & initialize as type long using BigOnes = long; // Defines BigOnes as a type alias using PhoneBook = std::map<std::shared_ptr<Contact>, std::string>;
Variable Scopes
* Global variables
* Static variables: defined within a block but continue to exist during program lifetime
int& f() { static int x = 0; return x; }
* External variables: variable defined in another source file
// File1.cpp int shared_value {100}; // Global variable // Other program code ... // File2.cpp extern int shared_value; // Declare variable to be external int main() { int local_value {shared_value + 10}; // Plus other code... }
Comparisons
* Comparison operators:
< <= > >= == !=
* Logical operators:
&& || !
Conditions
* if/else if/else
* switch
Arrays
double temperatures[366]; temperatures[3] = 70.0; unsigned int height[6] {}; unsigned int height[6] {26, 37, 47, 55, 62, 75}; // std::array<T, N> std::array<double, 100> values; values.fill(3.1415926); double total {}; for(size_t i {} ; i < values.size() ; ++i) { total += values[i]; //total += values.at(i); } for(auto value : values) { total += value; }
Loops
* for loop
int sum {}; for(size_t i {} ; i < sizeof (values)/sizeof (values[0]) ; ++i) { sum += values[i]; } int values [] {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; int total {}; for(int x : values) total += x;
* while loop
while(){ //continue; //break; } do{ }while()
Pointers and References
Pointer
* A pointer is a variable that can be used to store an address
// Define a pointer long* pnumber{nullptr};
Address-Of Operator: &
* Obtains the address of a variable
// Define a number long number {12345L}; // Assign address of number variable to pnumber pointer long* pnumber {&number};
Indirection Operator: *
* aka dereference operator
* Applying * to a ponter accesses the contents of the memory location to which it points
long number {12345L}; long* pnumber {nullptr}; pnumber = &number; std::cout << "Dereferencing, *pnumber = " << *pnumber << std::endl;
* Constant pointer pointing to constant array:
– Array elements cannot be modified
const char* const pstars[] { "Fatty Arbuckle", "Clara Bow", "Lassie", "Slim Pickens", "Boris Karloff", "Mae West", "Oliver Hardy", "Greta Garbo" };
Dynamic Memory Allocation
// declare a pointer of double type double* pvalue{nullptr}; // reserver memory for pvalue pvalue = new double; // assign value to pointer *pvalue = 3.14; // release memory reserved by pointer delete pvalue; pvalue = nullptr; // another way double* pval2 {nullptr}; pval2 = new double{3.14}; // release memory reserved by pointer delete pval2; pval2 = nullptr; // third way doulbe* pval3 {new double{3.14}); // release memory reserved by pointer delete pval3; pval3 = nullptr;
Smart Pointers
* A unique_ptr
– which means there cannot be more than one unique_ptr
std::unique_ptr<double> pdata {new double{999.0}}; // reset to nullptr pdata.reset();
* A shared_ptr
– in contrast with unique_ptr
std::shared_ptr<double> pdata {new double{999.0}}; // or better auto pdata = std::make_shared<double>(999.0);
* A weak_ptr
– Its memory will be released when the last shared_ptr
auto pData = std::make_shared<X>(); // Create a pointer to an object of type X std::weak_ptr<X> pwData {pData}; // Create a weak pointer from shared pointer std::weak_ptr<X> pwData2 {pwData}; // Create a weak pointer from another
References
* lvalue reference
double data {3.5}; // define lvlaue reference for data variable double& rdata {data}; // use exactly like data varilable (no need to dereference like using pointer) rdata += 2.5; // use reference to modify an array double temperatures[] {45.5, 50.0. 48.2. 57.0. 63.8}; const double F_to_C {5.0/9.0}; // Convert Fahrenheit to Centigrade for(auto& t : temperatures) // lvalue reference loop variable t = (t - 32.0)*FtoC;
Functions
* Syntax
return_type function_name (parameter_list) { // Code for the function... }
* Funciton prototypes
– Function prototypes are declared either in the beginning of source file or in header files
– Actual implementation can appear later in the source file
– So that they can be used in main()
// function prototype so that you can use this funciton in main() double power(double x, int n); int main(){ // use function } // Actual function implementaiton double power(double x, int n){ // implementation }
* Pass by pointer
#include <iostream> double change_it(double* pointer_to_it); int main(){ double it{5.0}; double result {change_it(&it)}; std::cout << "Outside function, result = " << result << std::endl; } double change_it(double* pit){ *pit += 10.0; std::cout << "Within function, *pit = " << *pit << std::endl; return *pit; }
* Pass by reference
* Arguments to main()
#include <iostream> int main(int argc, char* argv[]) { for (int i {} ; i < argc ; ++i) std::cout << i << ": " << argv[i] << std::endl; }
* Default arguments
// Define default argument value in function prototype void show_error(const string& message =Â "Program Error"); void show_error(const string& message) {Â Â std::cout << message << std::endl; } show_error(); // outputs "Program Error"
* Return values from a function
– never return address of a local variable from a function
– never return a reference to a local variable from a function
string& larger(string& s1, string& s2) { return s1 > s2 ? s1 : s2; // Return a reference to the larger string }
* Inline functions
– place them in header files
– it signals that compiler can rewrite the function for performance gains
inline int larger(int m, int n) { return m > n ? m : n; }
* static variables inside a function
– so that variable values can be retains throughout runtime
void nextInteger() { static int count {1}; // retains thru entire runtime std::cout << count++ << std::endl; }
* Function overloading
* Function template
template <typename T> T larger(T a, T b); // Function template prototype int main(){ std::cout << "Larger of 1.5 and 2.5 is " << larger(1.5, 2.5) << std::endl; std::cout << "Larger of 3.5 and 4.5 is " << larger(3.5, 4.5) << std::endl; // explict template argument std::cout << "Larger of " << a_long << " and 9.5 is " << larger<double>(a_long, 9.5) << std::endl; // Outputs 9.5 } // Template for functions to return the larger of two values template <typename T> T larger(T a, T b) { return a > b ? a : b; } // Specialization of the template template <> long* larger<long*>(long* a, long* b) { return *a > *b ? a : b; }
Lamda Expressions
* A lamda expression is an anonymouse function
* Mainly used to pass a function as argument to another function
– it can access variables that exist in the enclosing scope where it is defined so an alternative to function pointer
* [], called lamda introducer, signifies a lamda expression
// define a lamda expression [] (double value) { return value*value*value; } // specify return type [] (double value) -> double { return value*value*value; } // execute lamda expression and assign result to a variable double cube {}; cube = [] (double value) -> double { return value*value*value; }(3.5); // 42.875 // Use lamda expression to initialize a variable cube {[] (double value) -> double { return value*value*value; }(3.5)}; // 42.875 // Assign lamda expression to a variable auto cube = [] (double value) -> double { return value*value*value; };
* Passing a lamda expression to a function
#include <iostream> #include <vector> #include <functional> // Define a function template that accept a function as argument // F type matches any function that accepts double as argument and returns double template <typename F> std::vector<double>& change(std::vector<double>& vec, F func){ for (auto& x : vec){ x = func(x); } return vec; } // Same as using function pointer std::vector<double>& change2(std::vector<double>& vec, double(*func)(double)){ for (auto& x : vec){ x = func(x); } return vec; } // Same but using std::function template type std::vector<double>& change3(std::vector<double>& vec, std::function<double(double)> fun) { for (auto& x : vec) x = fun(x); return vec; } int main(){ // Define cube lamda expression auto cube = [](double value) -> double {return value*value*value;}; // Define average lamda expression auto average = [](const std::vector<double>& v) -> double{ double sum{}; for (auto x : v){ sum += x; } return sum / v.size(); }; std::vector<double> data {1.5, 2.5, 3.5, 4.5, 5.5}; std::cout << "Average of values in data is " << average(data) << std::endl; // Using function template and passing in lamda expression as function argument change(data, [] (double x){return (x + 1.0)*(x + 2.0);}); std::cout << "Average of changed values in data is " << average(data) << std::endl; // Using function template and passing in cube lamda expression as function argument std::cout << "Average of cubes of values in data is " << average(change3(data, cube)) << std::endl; }
* Lamda function capture clause
// []: stateless lambda expression // lambda exp body cannot access any variables in its enclosing scope [] (double value) { return value*value*value; } // [=]: lambda exp body can access all variable values in its enclosing scope // cannot change variable values though std::vector<double> data {1.5, 2.5, 3.5, 4.5, 5.5}; double factor {10.0}; change(data, [=](double x){ return factor*x; }); // can access factor by value; cannot change factor's value std::cout << "The values in data are now:\n" for(const auto& x : data) std::cout << " " << x; std::cout << std::endl; //[&]: same as [=] but can change variable values change(data, [&](double x) { factor += 2.0; // factor value can be changed here return factor*x; } ); //[&var]: same as [&] but only var value can be changed change(data, [&factor](double x) { factor += 2.0; // factor value can be changed here, all other variables cannot be chagned return factor*x; } ); //[=, &var]: only var value can be changed, all other variables can be accessed by values change(data, [=, &factor](double x) { factor += 2.0; // factor value can be changed here, all other variables cannot be chagned return factor*x; } ); //[&, var]: all variables can be changed, var can only be accessed by value change(data, [&, factor](double x) { factor += 2.0; // factor value can be changed here, all other variables cannot be chagned return factor*x; } );
Preprocessing Directives
* Translation unit: each source file + included header files
* Translation unit -compiler-> object file -linker-> executable file
* One definition rule: each variable, function, class type, enumeration type, or template in a translation unit must only be defined once.
Linkage for a Name
* Internal linkage: can be accessed from anywhere within the same translation unit but NOT outside, e.g. global scope const var
const double pi {3.14159265};
* External linkage: can be accessed from another translation unit in addition to the one in which it is defined
* No linkage: can be accessed from within the scope that applies to the name, e.g. names defined within a block.
extern const double pi {3.14159265};
* Access variable name defined outside current translation unit:
extern double pi; // pi has been declared outside current translation unit already but we want to use it here
Preprocessing Source Code
#include #include <iostream> // iostream header contents are included #include "myheader.h" // include myheader.h file in the current directory #define #define BLACK WHITE // Global replacement of BLACK with WHITE in source code #define BLACK // Global removal of BLACK from source code #undef #undef BLACK // Stop global replacements of BLACK #if #elif #else #endif #if defined // Same as: #ifdef #if !defined // Same as: #ifndef // Usage: #ifndef MYHEADER_H #define MYHEADER_H #include "myheader.h" #endif #line #error #pragma
Using Header Files
* Ex10_01.h
#include <string> #ifndef EX10_01_H #define EX10_01_H namespace data { extern const double pi {3.14159265}; extern const std::string days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; } #endif
* Ex10_01.cpp
#include <iostream> #include <string> #include "Ex10_01.h" // Include the header file int main() { std::cout << "pi has the value " << data::pi << std::endl; std::cout << "The second day of the week is " << data::days[1] << std::endl; }
Classes
* Syntax
class ClassName { private: // Code that specifies members that are not accessible from outside the class protect: // Code that specifies members that are accessible from subclasses public: // Code that specifies members that are accessible from outside the class }; // Note the semicolon here!
* Constructor
– using the explicit keyword with constructors that have a single parameter to prevent implicit conversions from the parameter type to the class type
class Cube { public: double side; explicit Cube(double side); // Explicit constructor double volume(); // Calculate volume of a cube bool compareVolume(Cube aCube); // Compare volume of a cube with another };
* Destructor
~Cube() { // Cleanup... }
* Accessing member of a class using pointer
a->b // is equivalent to: (*a).b
* this pointer
double Box::volume() { return this->length * this->width * this->height; }
* Static members of a class
* Pointers and references to class objectds
* Using Pointers as class members
* Example
– Box.h
#ifndef BOX_H #define BOX_H #include <iostream> #include <iomanip> class Box { // Private memebers private: double length {1.0}; double width {1.0}; double height {1.0}; // Public members public: // ======================= Constructors // Default constructor Box() {} // Using constructor initialization list Box(double lv, double wv, double hv): length{lv}, width {wv}, height {hv} {} // Copy constructor, use const so we cannot modify object to be copied Box (const Box& box) { length = box.length; width = box.width; height = box.height; } // ======================= Getters and setters // Getters double getLength() {return length;} double getWidth() {return width;} double getHeight() {return height;} // Setters void setLength(double lv) { if(lv > 0) length = lv;} void setWidth(double wv) { if(wv > 0) width = wv;} void setHeight(double hv) { if(hv > 0) height = hv; } // ======================= Methods double volume() const { return length * width * height; } int compare (const Box& box) { if (volume() < box.volume()) return -1; if (volume() == box.volume()) return 0; return 1; } void listBox() { std::cout << " Box(" << std::setw(2) << length << "," << std::setw(2) << width << "," << std::setw(2) << height << ")"; } }; #endif
– Testbox.cpp
#include <iostream> #include "Box.h" int main(){ const Box box1 {2.0, 3.0, 4.0}; std::cout << "Volume: " << box1.volume() << std::endl; }
Operator Overloading
*
Inheritance
Polymorphism
Runtime Errors and Exception
Class Templates
File IO
References
* Beginning C++ by Ivor Horton
* Book source code
* Standard C++ Library reference
* C++ Standard Library header files