Book Note: Beginning C++

 

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(expression)

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 object behaves as a pointer to type T and is unique,
– which means there cannot be more than one unique_ptr object containing the same address

std::unique_ptr<double> pdata {new double{999.0}};
// reset to nullptr
pdata.reset();

* A shared_ptr object behaves as a pointer to type T,
– in contrast with unique_ptr, there can be any number of shared_ptr objects containing the same address.

std::shared_ptr<double> pdata {new double{999.0}};
// or better
auto pdata = std::make_shared<double>(999.0);

* A weak_ptr is linked to a shared_ptr and contains the same address.
– Its memory will be released when the last shared_ptr referencing it is destroyed or reassigned to point to a different address, even though associated weak_ptr objects may still exist.

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

This entry was posted in cpp and tagged . Bookmark the permalink.