C++

C++语言

现代C++设计新思维

第一篇 技术(Techniques)

第1章 基于Policy的Class设计(Policy-Based Class Design)

第2章 技术(Techniques)

第3章 Typelists

第4章 小型对象分配技术(Small-Object Allocation)

第二篇 组件(Component)

第5章 泛化仿函数(Generalized Functions)

第6章 Singletons(单件)实作技术

第7章 Smart Pointers(智能指针)

第8章 Object Factiories(对象工厂)

第9章 Abstract Factory(抽象工厂)

第10章 Visitor(访问者,视察者)

第11章 Multimethods

附录 一个超迷你的多线程程序库(A Minimalist Multithreading Library)

C++源码摘要
// This is a source version of the cpp cheatsheet available here. Note that this does not compile but may have better
// color-highlight than the markdown version in an editor.
//
// Github version available here: https://github.com/mortennobel/cpp-cheatsheet

// # C++ QUICK REFERENCE / C++ CHEATSHEET
// Based on <a href="http://www.pa.msu.edu/~duxbury/courses/phy480/Cpp_refcard.pdf">Phillip M. Duxbury's C++ Cheatsheet</a> and edited by Morten Nobel-Jørgensen.
// The cheatsheet focus is both on the language as well as common classes from the standard library.
// C++11 additions is inspired by <a href="https://isocpp.org/blog/2012/12/c11-a-cheat-sheet-alex-sinyakov">ISOCPP.org C++11 Cheatsheet</a>).
//
// The goal is to give a concise overview of basic, modern C++ (C++14).
//
// The document is hosted on https://github.com/mortennobel/cpp-cheatsheet. Any comments and feedback are appreciated.

// ## Preprocessor

// Comment to end of line
/* Multi-line comment */
#include  <stdio.h>         // Insert standard header file
#include "myfile.h"         // Insert file in current directory
#define X some text         // Replace X with some text
#define F(a,b) a+b          // Replace F(1,2) with 1+2
#define X \
 some text                  // Multiline definition
#undef X                    // Remove definition
#if defined(X)              // Conditional compilation (#ifdef X)
#else                       // Optional (#ifndef X or #if !defined(X))
#endif                      // Required after #if, #ifdef


// ## Literals

255, 0377, 0xff             // Integers (decimal, octal, hex)
2147483647L, 0x7fffffffl    // Long (32-bit) integers
123.0, 1.23e2               // double (real) numbers
'a', '\141', '\x61'         // Character (literal, octal, hex)
'\n', '\\', '\'', '\"'      // Newline, backslash, single quote, double quote
"string\n"                  // Array of characters ending with newline and \0
"hello" "world"             // Concatenated strings
true, false                 // bool constants 1 and 0
nullptr                     // Pointer type with the address of 0

// ## Declarations

int x;                      // Declare x to be an integer (value undefined)
int x=255;                  // Declare and initialize x to 255
short s; long l;            // Usually 16 or 32 bit integer (int may be either)
char c='a';                 // Usually 8 bit character
unsigned char u=255;
signed char s=-1;           // char might be either
unsigned long x =
        0xffffffffL;              // short, int, long are signed
float f; double d;          // Single or double precision real (never unsigned)
bool b=true;                // true or false, may also use int (1 or 0)
int a, b, c;                // Multiple declarations
int a[10];                  // Array of 10 ints (a[0] through a[9])
int a[]={0,1,2};            // Initialized array (or a[3]={0,1,2}; )
int a[2][2]={{1,2},{4,5}};  // Array of array of ints
char s[]="hello";           // String (6 elements including '\0')
std::string s = "Hello"     // Creates string object with value "Hello"
std::string s = R"(Hello
World)";                    // Creates string object with value "Hello\nWorld"
int* p;                     // p is a pointer to (address of) int
char* s="hello";            // s points to unnamed array containing "hello"
void* p=nullptr;            // Address of untyped memory (nullptr is 0)
int& r=x;                   // r is a reference to (alias of) int x
enum weekend {SAT,SUN};     // weekend is a type with values SAT and SUN
enum weekend day;           // day is a variable of type weekend
enum weekend{SAT=0,SUN=1};  // Explicit representation as int
enum {SAT,SUN} day;         // Anonymous enum
enum class Color {Red,Blue};// Color is a strict type with values Red and Blue
Color x = Color::Red;       // Assign Color x to red
typedef String char*;       // String s; means char* s;
const int c=3;              // Constants must be initialized, cannot assign to
const int* p=a;             // Contents of p (elements of a) are constant
int* const p=a;             // p (but not contents) are constant
const int* const p=a;       // Both p and its contents are constant
const int& cr=x;            // cr cannot be assigned to change x
int8_t,uint8_t,int16_t,
uint16_t,int32_t,uint32_t,
int64_t,uint64_t            // Fixed length standard types
auto it = m.begin();        // Declares it to the result of m.begin()
auto const param = config["param"];
// Declares it to the const result
auto& s = singleton::instance();
// Declares it to a reference of the result


// ## STORAGE Classes

int x;                      // Auto (memory exists only while in scope)
static int x;               // Global lifetime even if local scope
extern int x;               // Information only, declared elsewhere


// ## Statements


x=y;                        // Every expression is a statement
int x;                      // Declarations are statements
;                           // Empty statement
{                           // A block is a single statement
    int x;                  // Scope of x is from declaration to end of block
}
if (x) a;                   // If x is true (not 0), evaluate a
else if (y) b;              // If not x and y (optional, may be repeated)
else c;                     // If not x and not y (optional)

while (x) a;                // Repeat 0 or more times while x is true

for (x; y; z) a;            // Equivalent to: x; while(y) {a; z;}

for (x : y) a;              // Range-based for loop e.g.
// for (auto& x in someList) x.y();

do a; while (x);            // Equivalent to: a; while(x) a;

switch (x) {                // x must be int
case X1: a;             // If x == X1 (must be a const), jump here
case X2: b;             // Else if x == X2, jump here
default: c;             // Else jump here (optional)
}
break;                      // Jump out of while, do, or for loop, or switch
continue;                   // Jump to bottom of while, do, or for loop
return x;                   // Return x from function to caller
try { a; }
catch (T t) { b; }          // If a throws a T, then jump here
catch (...) { c; }          // If a throws something else, jump here

// ## Functions

int f(int x, int y);        // f is a function taking 2 ints and returning int
void f();                   // f is a procedure taking no arguments
void f(int a=0);            // f() is equivalent to f(0)
f();                        // Default return type is int
inline f();                 // Optimize for speed
f() { statements; }         // Function definition (must be global)
T operator+(T x, T y);      // a+b (if type T) calls operator+(a, b)
T operator-(T x);           // -a calls function operator-(a)
T operator++(int);          // postfix ++ or -- (parameter ignored)
extern "C" {void f();}      // f() was compiled in C

// Function parameters and return values may be of any type. A function must either be declared or defined before
// it is used. It may be declared first and defined later. Every program consists of a set of a set of global variable
// declarations and a set of function definitions (possibly in separate files), one of which must be:


int main()  { statements... }     // or
int main(int argc, char* argv[]) { statements... }


// `argv` is an array of `argc` strings from the command line.
// By convention, `main` returns status `0` if successful, `1` or higher for errors.
//
// Functions with different parameters may have the same name (overloading). Operators except `::` `.` `.*` `?:` may be overloaded.
// Precedence order is not affected. New operators may not be created.

// ## Expressions

// Operators are grouped by precedence, highest first. Unary operators and assignment evaluate right to left. All
// others are left to right. Precedence does not affect order of evaluation, which is undefined. There are no run time
// checks for arrays out of bounds, invalid pointers, etc.

T::X                        // Name X defined in class T
N::X                        // Name X defined in namespace N
::X                         // Global name X
t.x                         // Member x of struct or class t
p-> x                       // Member x of struct or class pointed to by p
a[i]                        // i'th element of array a
f(x,y)                      // Call to function f with arguments x and y
T(x,y)                      // Object of class T initialized with x and y
x++                         // Add 1 to x, evaluates to original x (postfix)
x--                         // Subtract 1 from x, evaluates to original x
typeid(x)                   // Type of x
typeid(T)                   // Equals typeid(x) if x is a T
dynamic_cast< T>(x)         // Converts x to a T, checked at run time.
static_cast< T>(x)          // Converts x to a T, not checked
reinterpret_cast< T>(x)     // Interpret bits of x as a T
const_cast< T>(x)           // Converts x to same type T but not const

sizeof x                    // Number of bytes used to represent object x
sizeof(T)                   // Number of bytes to represent type T
++x                         // Add 1 to x, evaluates to new value (prefix)
--x                         // Subtract 1 from x, evaluates to new value
~x                          // Bitwise complement of x
!x                          // true if x is 0, else false (1 or 0 in C)
-x                          // Unary minus
+x                          // Unary plus (default)
&x                          // Address of x
*p                          // Contents of address p (*&x equals x)
new T                       // Address of newly allocated T object
new T(x, y)                 // Address of a T initialized with x, y
new T[x]                    // Address of allocated n-element array of T
delete p                    // Destroy and free object at address p
delete[] p                  // Destroy and free array of objects at p
(T) x                       // Convert x to T (obsolete, use .._cast<T>(x))

x * y                       // Multiply
x / y                       // Divide (integers round toward 0)
x % y                       // Modulo (result has sign of x)

x + y                       // Add, or \&x[y]
x - y                       // Subtract, or number of elements from *x to *y
x << y                      // x shifted y bits to left (x * pow(2, y))
x >> y                      // x shifted y bits to right (x / pow(2, y))

x < y                       // Less than
x <= y                      // Less than or equal to
x > y                       // Greater than
x >= y                      // Greater than or equal to

x & y                       // Bitwise and (3 & 6 is 2)
x ^ y                       // Bitwise exclusive or (3 ^ 6 is 5)
x | y                       // Bitwise or (3 | 6 is 7)
x && y                      // x and then y (evaluates y only if x (not 0))
x || y                      // x or else y (evaluates y only if x is false (0))
x = y                       // Assign y to x, returns new value of x
x += y                      // x = x + y, also -= *= /= <<= >>= &= |= ^=
x ? y : z                   // y if x is true (nonzero), else z
throw x                     // Throw exception, aborts if not caught
x , y                       // evaluates x and y, returns y (seldom used)

// ## Classes

class T {                   // A new type
private:                    // Section accessible only to T's member functions
protected:                  // Also accessible to classes derived from T
public:                     // Accessible to all
    int x;                  // Member data
    void f();               // Member function
    void g() {return;}      // Inline member function
    void h() const;         // Does not modify any data members
    int operator+(int y);   // t+y means t.operator+(y)
    int operator-();        // -t means t.operator-()
    T(): x(1) {}            // Constructor with initialization list
    T(const T& t): x(t.x) {}// Copy constructor
    T& operator=(const T& t)
    {x=t.x; return *this; } // Assignment operator
    ~T();                   // Destructor (automatic cleanup routine)
    explicit T(int a);      // Allow t=T(3) but not t=3
    T(float x): T((int)x) {}// Delegate constructor to T(int)
    operator int() const
    {return x;}             // Allows int(t)
    friend void i();        // Global function i() has private access
    friend class U;         // Members of class U have private access
    static int y;           // Data shared by all T objects
    static void l();        // Shared code.  May access y but not x
    class Z {};             // Nested class T::Z
    typedef int V;          // T::V means int
};
void T::f() {               // Code for member function f of class T
    this->x = x;}           // this is address of self (means x=x;)
int T::y = 2;               // Initialization of static member (required)
T::l();                     // Call to static member
T t;                        // Create object t implicit call constructor
t.f();                      // Call method f on object t

struct T {                  // Equivalent to: class T { public:
    virtual void i();         // May be overridden at run time by derived class
    virtual void g()=0; };    // Must be overridden (pure virtual)
class U: public T {         // Derived class U inherits all members of base T
public:
    void g(int) override; };  // Override method g
class V: private T {};      // Inherited members of T become private
class W: public T, public U {};
// Multiple inheritance
class X: public virtual T {};
// Classes derived from X have base T directly

// All classes have a default copy constructor, assignment operator, and destructor, which perform the
// corresponding operations on each data member and each base class as shown above. There is also a default no-argument
// constructor (required to create arrays) if the class has no constructors. Constructors, assignment, and
// destructors do not inherit.

// ## Templates

template <class T> T f(T t);// Overload f for all types
template <class T> class X {// Class with type parameter T
    X(T t); };                // A constructor
template <class T> X<T>::X(T t) {}
// Definition of constructor
X<int> x(3);                // An object of type "X of int"
template <class T, class U=T, int n=0>
// Template with default parameters

// ## Namespaces

namespace N {class T {};}   // Hide name T
N::T t;                     // Use name T in namespace N
using namespace N;          // Make T visible without N::

// ## `memory` (dynamic memory management)

#include <memory>           // Include memory (std namespace)
shared_ptr<int> x;          // Empty shared_ptr to a integer on heap. Uses reference counting for cleaning up objects.
x = make_shared<int>(12);   // Allocate value 12 on heap
shared_ptr<int> y = x;      // Copy shared_ptr, implicit changes reference count to 2.
cout << *y;                 // Dereference y to print '12'
if (y.get() == x.get()) {   // Raw pointers (here x == y)
  cout << "Same";
}
y.reset();                  // Eliminate one owner of object
if (y.get() != x.get()) {
  cout << "Different";
}
if (y == nullptr) {         // Can compare against nullptr (here returns true)
  cout << "Empty";
}
y = make_shared<int>(15);   // Assign new value
cout << *y;                 // Dereference x to print '15'
cout << *x;                 // Dereference x to print '12'
weak_ptr<int> w;            // Create empty weak pointer
w = y;                      // w has weak reference to y.
if (shared_ptr<int> s = w.lock()) { // Has to be copied into a shared_ptr before usage
  cout << *s;
}
unique_ptr<int> z;          // Create empty unique pointers
unique_ptr<int> q;
z = make_unique<int>(16);   // Allocate int (16) on heap. Only one reference allowed.
q = move(z);                // Move reference from z to q.
if (z == nullptr){
  cout << "Z null";
}
cout << *q;
shared_ptr<B> r;
r = dynamic_pointer_cast<B>(t); // Converts t to a shared_ptr<B>


// ## `math.h`, `cmath` (floating point math)

#include <cmath>            // Include cmath (std namespace)
sin(x); cos(x); tan(x);     // Trig functions, x (double) is in radians
asin(x); acos(x); atan(x);  // Inverses
atan2(y, x);                // atan(y/x)
sinh(x); cosh(x); tanh(x);  // Hyperbolic sin, cos, tan functions
exp(x); log(x); log10(x);   // e to the x, log base e, log base 10
pow(x, y); sqrt(x);         // x to the y, square root
ceil(x); floor(x);          // Round up or down (as a double)
fabs(x); fmod(x, y);        // Absolute value, x mod y

// ## `assert.h`, `cassert` (Debugging Aid)

#include <cassert>        // Include iostream (std namespace)
        assert(e);                // If e is false, print message and abort
#define NDEBUG            // (before #include <assert.h>), turn off assert

// ## `iostream.h`, `iostream` (Replaces `stdio.h`)

#include <iostream>         // Include iostream (std namespace)
cin >> x >> y;              // Read words x and y (any type) from stdin
cout << "x=" << 3 << endl;  // Write line to stdout
cerr << x << y << flush;    // Write to stderr and flush
c = cin.get();              // c = getchar();
cin.get(c);                 // Read char
cin.getline(s, n, '\n');    // Read line into char s[n] to '\n' (default)
if (cin)                    // Good state (not EOF)?
// To read/write any type T:
istream& operator>>(istream& i, T& x) {i >> ...; x=...; return i;}
ostream& operator<<(ostream& o, const T& x) {return o << ...;}

// ## `fstream.h`, `fstream` (File I/O works like `cin`, `cout` as above)

#include <fstream>          // Include filestream (std namespace)
ifstream f1("filename");    // Open text file for reading
if (f1)                     // Test if open and input available
f1 >> x;                    // Read object from file
f1.get(s);                  // Read char or line
f1.getline(s, n);           // Read line into string s[n]
ofstream f2("filename");    // Open file for writing
if (f2) f2 << x;            // Write to file

// ## `string` (Variable sized character array)

#include <string>         // Include string (std namespace)
string s1, s2="hello";    // Create strings
s1.size(), s2.size();     // Number of characters: 0, 5
s1 += s2 + ' ' + "world"; // Concatenation
s1 == "hello world"       // Comparison, also <, >, !=, etc.
s1[0];                    // 'h'
s1.substr(m, n);          // Substring of size n starting at s1[m]
s1.c_str();               // Convert to const char*
s1 = to_string(12.05);    // Converts number to string
getline(cin, s);          // Read line ending in '\n'

// ## `vector` (Variable sized array/stack with built in memory allocation)

#include <vector>         // Include vector (std namespace)
vector<int> a(10);        // a[0]..a[9] are int (default size is 0)
vector<int> b{1,2,3};        // Create vector with values 1,2,3
a.size();                 // Number of elements (10)
a.push_back(3);           // Increase size to 11, a[10]=3
a.back()=4;               // a[10]=4;
a.pop_back();             // Decrease size by 1
a.front();                // a[0];
a[20]=1;                  // Crash: not bounds checked
a.at(20)=1;               // Like a[20] but throws out_of_range()
for (int& p : a)
p=0;                    // C++11: Set all elements of a to 0
for (vector<int>::iterator p=a.begin(); p!=a.end(); ++p)
*p=0;                   // C++03: Set all elements of a to 0
vector<int> b(a.begin(), a.end());  // b is copy of a
vector<T> c(n, x);        // c[0]..c[n-1] init to x
T d[10]; vector<T> e(d, d+10);      // e is initialized from d

// ## `deque` (Array stack queue)

// `deque<T>` is like `vector<T>`, but also supports:

#include <deque>          // Include deque (std namespace)
a.push_front(x);          // Puts x at a[0], shifts elements toward back
a.pop_front();            // Removes a[0], shifts toward front

// ## `utility` (pair)

#include <utility>        // Include utility (std namespace)
        pair<string, int> a("hello", 3);  // A 2-element struct
a.first;                  // "hello"
a.second;                 // 3

// ## `map` (associative array - usually implemented as binary search trees - avg. time complexity: O(log n))

#include <map>            // Include map (std namespace)
map<string, int> a;       // Map from string to int
a["hello"] = 3;           // Add or replace element a["hello"]
for (auto& p:a)
    cout << p.first << p.second;  // Prints hello, 3
a.size();                 // 1

// ## `unordered_map` (associative array - usually implemented as hash table - avg. time complexity: O(1))

#include <unordered_map>  // Include map (std namespace)
unordered_map<string, int> a; // Map from string to int
a["hello"] = 3;           // Add or replace element a["hello"]
for (auto& p:a)
cout << p.first << p.second;  // Prints hello, 3
a.size();                 // 1

// ## `set` (store unique elements - usually implemented as binary search trees - avg. time complexity: O(log n))

#include <set>            // Include set (std namespace)
set<int> s;               // Set of integers
s.insert(123);            // Add element to set
if (s.find(123) != s.end()) // Search for an element
s.erase(123);
cout << s.size();         // Number of elements in set

// ## `unordered_set` (store unique elements - usually implemented as a hash set - avg. time complexity: O(1))

#include <unordered_set>  // Include set (std namespace)
unordered_set<int> s;     // Set of integers
s.insert(123);            // Add element to set
if (s.find(123) != s.end()) // Search for an element
s.erase(123);
cout << s.size();         // Number of elements in set

// ## `algorithm` (A collection of 60 algorithms on sequences with iterators)

#include <algorithm>      // Include algorithm (std namespace)
min(x, y); max(x, y);     // Smaller/larger of x, y (any type defining <)
swap(x, y);               // Exchange values of variables x and y
sort(a, a+n);             // Sort array a[0]..a[n-1] by <
sort(a.begin(), a.end()); // Sort vector or deque
reverse(a.begin(), a.end()); // Reverse vector or deque

// ## `chrono` (Time related library)

#include <chrono>         // Include chrono
using namespace std::chrono; // Use namespace
auto from =               // Get current time_point
        high_resolution_clock::now();
// ... do some work       
auto to =                 // Get current time_point
        high_resolution_clock::now();
using ms =                // Define ms as floating point duration
duration<float, milliseconds::period>;
// Compute duration in milliseconds
cout << duration_cast<ms>(to - from)
.count() << "ms";

// ## `thread` (Multi-threading library)

#include <thread>         // Include thread
unsigned c =
        hardware_concurrency(); // Hardware threads (or 0 for unknown)
auto lambdaFn = [](){     // Lambda function used for thread body
    cout << "Hello multithreading";
};
thread t(lambdaFn);       // Create and run thread with lambda
t.join();                 // Wait for t finishes

// --- shared resource example ---
mutex mut;                         // Mutex for synchronization
condition_variable cond;           // Shared condition variable
const char* sharedMes              // Shared resource
        = nullptr;
auto pingPongFn =                  // thread body (lambda). Print someone else's message
        [&](const char* mes){
            while (true){
                unique_lock<mutex> lock(mut);// locks the mutex
                do {
                    cond.wait(lock, [&](){     // wait for condition to be true (unlocks while waiting which allows other threads to modify)
                        return sharedMes != mes; // statement for when to continue
                    });
                } while (sharedMes == mes);  // prevents spurious wakeup
                cout << sharedMes << endl;
                sharedMes = mes;
                lock.unlock();               // no need to have lock on notify
                cond.notify_all();           // notify all condition has changed
            }
        };
sharedMes = "ping";
thread t1(pingPongFn, sharedMes);  // start example with 3 concurrent threads
thread t2(pingPongFn, "pong");
thread t3(pingPongFn, "boing");

// ## `future` (thread support library)

#include <future>         // Include future
        function<int(int)> fib =  // Create lambda function
[&](int i){
    if (i <= 1){
        return 1;
    }
    return fib(i-1)
           + fib(i-2);
};
future<int> fut =         // result of async function
        async(launch::async, fib, 4); // start async function in other thread
// do some other work 
cout << fut.get();        // get result of async function. Wait if needed.
cpp_cheatsheet

INDEX

  1. Variable and fundamental data types
    1. Initializing a variable
    2. Intergers
    3. Floating point numbers
    4. Boolean Numbers
    5. Type casting
    6. Literal
    7. Constants
    8. Variable Scopes and duration
      1. Shadowing
      2. Global scope operator
  2. Console Input/Output
    1. Console I/O Methods
    2. Error Handling in Console Input
    3. A Program Handling All the Error Case in Input
  3. Generating Random Numbers
    1. Generating Random Numbers
    2. Function for generating Random Numbers Between A Range
  4. Advanced Data Types
    1. Array
      1. Array Indexes
      2. Representing Array Indexes with Enumerators
      3. Relation Between Array and Pointer
      4. Difference Between Array and Pointer to Array
      5. String Constants Using Fixed Sized Array and Pointer
    2. Pointers
      1. Definig NULL Pointer C++ Way
      2. Void Pointers
      3. Converting A Pointer Address to Integer
  5. Dynamic Memory Allocation
    1. Allocating Memory dynamically
    2. Deallocating memory
    3. Memory leak
  6. Reference Variables
  7. For Each Loop
  8. Functions
    1. Function Pointers
    2. Function Ellipsis
    3. Lambda Functios
  9. Object Oriented Programming
    1. Basic Class Example
    2. Constructors
    3. Destructors
    4. Static Member Variables
    5. Static Member Functions
    6. Member Type or Nested Type
    7. Access Specifiers
    8. Chaining member functions
    9. Difference between structs and classes in C++
    10. Friend function and friend class
  10. Operator Overloading
    1. Using Friend Function
    2. Using Normal Function
    3. Using Member Function
    4. Overloading IO operators
    5. Functors
    6. Overloading Typecast
    7. Copy Constructors
      1. Elision
      2. Implicit conversion, explicit and delete keyword
    8. Overloading assignment operator
    9. Shallow copy VS Deep copy
  11. Object Relationship
    1. Composition and Aggregation
      1. Composition
      2. Aggregation
    2. Association
    3. Dependencies
    4. Container Classes
    5. std::initializer_list
  12. Inheritance
    1. Order of construction
    2. Constructors and initialization of derived classes
    3. Order of Destruction
    4. Inheritance and Access Specifiers
    5. Overriding Behavior
      1. Redefining
      2. Expanding Existing Functionality
      3. Changing an Inherited Member’s Access Specification
    6. Multiple Inheritance
  13. Virtual Functions
    1. Pointers to the Base Class of Derived Object
    2. Polymorphism
    3. override and final Specifiers
    4. Covariant Return Type
    5. Abstract Class, Pure Virtual Functions and Interface Class
    6. Virtual Base Class
    7. Object Slicing
      1. std::reference_wrapper
    8. Dynamic Casting
    9. Workaround for Friend Functions
  14. Templates
    1. Function Template
    2. Class Template
    3. Template Non-Type parameters
    4. Template Specialization

Variable and fundamental data types

Initializing a variable

  • Copy initialization:
int numRings = 20;
  • Direct initialization:
int numRings(20);
  • Uniform initialization:
int numRings{20}

If uniform initialization is used with no value. Default is 0.

int numRings{}

Integers

Fixed width integers

Will give fixed sized integer in all architecture.

#include <cstdint> // for fixed width integers

/* 
 * 8 bit singed integer. Many systems consider them as chars. So it is better
 * to not use them if using integers is the purpose
 */
int8_t var;

uint8_t var;  // 8 bit unsigned integer

intN_t var; // N = 16, 32, 64 bits signed integer
uintN_t var;  // N = 16, 32, 64 bits unsigned integer

Fixed width integers performance is machine dependent. To get the fastest integer type on a specific machine use int_fastN_t. It will give the fastest integer which is at least N bits long. N = 8, 16, 32, 64.

int_fast32_t var;

To get the smallest integer which is at least N bits long use int_leastN_t where N = 8, 16, 32, 64.

int_least64_t var;

Floating point numbers

Setting precision of a floating point number:

#include <iostream>
#include <iomanip> // for std::setprecision()

int main(){
  double d{12.34567890};

  std::cout << "Without precision: " << d << std::endl;
  std::cout << std::setprecision(4);
  std::cout << "With precision: " << d << std::endl;

  return 0;
}

Special floating numbers: positive infinity, negative infinity, not a number.

#include <iostream>

int main(){
  float zero(0.0);
  float posinf = 5.0 / zero;
  float neginf = -5.0 / zero;
  float nan = zero / zero;

  std::cout << posinf << std::endl;
  std::cout << neginf << std::endl;
  std::cout << nan << std::endl;

  return 0;
}

Boolean Numbers

#include <iostream>

int main(){
  bool universeCameFromNothing(true);

  std::cout << "Universe came from nothing: " << universeCameFromNothing
    << std::endl;

  std::cout << std::boolalpha;
  
  std::cout << "Universe came from nothing: " << universeCameFromNothing
    << std::endl;

  return 0;
}

Type Casting

#include <iostream>

int main(){
  char ch(65);

  std::cout << ch << std::endl;
  std::cout << static_cast<int>(ch) << std::endl;

  return 0;
}

To get the variable or expression type.

#include <typeinfo>

int main(){
  int numerator(50);
  double denominator(5.0);

  std::cout << typeid(numerator).name() << std::endl;
  std::cout << typeid(numerator / denominator).name() << std::endl;
}

Literal

int integer(50);  // integer
float f(0.05f);   // used f suffix as default literal is double type
double d(0.31416);  // double floating literal
int hex(0xa0);    // hexadecimal literal
int oct(012);   // Octal literal
int bin(0b1010);  // Binary literal

int longNumber(1'23'570) // c++14 only

Constants

const double avg(6.023e23);
const double massEarth;   // This is not allowed

int x(50);
const int y(x)      // This is allowed

constexpr double adg(9.8);  // Compile time constant. Value of the constant
        // must be resolved in compile time otherwise
        // will generate an error. This is c++11 feature

Variable Scopes and duration

shadowing:

int number(5);

if(number == 5){
  int number;   // local variable

  number = 10;  // will effect only local variable
      // this is shadowing

  std::cout << number << std::endl; // will print local
}

std::cout << number << std::endl; // will print outer block number

Global scope operator

:: is the global scope operator.

int x(50);  // global variable

int main(){
  int x(40);

  std::cout << x << std::endl;  // will print local x
  std::cout << ::x << std::endl;  // will print global x
}

Internal Variable: Can be used anywhere in the file they are defined but not out of the file.

External Variable: Cab be used across multiple files. Global variables are by default external. static keyword can be used to make them internal.

Console Input/Output

Console I/O Methods

  • std::cin is used for console input. std::cin takes inputs untill first whitespace.
#include <iostream>

int main(){
  int selection;

  std::cin >> selection;
  std::cout << "You have selected: " << selection << std::endl;

  return 0;
}
  • std::getline() is used to take whole line as input.
#include <iostream>
#include <string>

int main(){
  std::string name;

  std::cout << "Enter Name: ";
  std::getline(std::cin, name);
  std::cout << "Your name is: " << name;

  return 0;
}

Error Handling in Console Input

std::cin takes upto newline from the input stream. So it will be a problem if any other input function is used after taking numeric input from std::cin.

#include <iostream>
#include <string>

int main(){
  std::string name;
  int select;

  std::cout << "Select: ";
  std::cin >> select;

  std::cout << "Enter name: ";
  std::getline(std::cin, name);   // will not work
 
  std::cout << "Your name is: " << name << "! You have selected: "
      << select;

  return 0;
}

To solve this problem std::cin.ignore(n, ch) can be used where n is the number of character to ignore from the input stream before ch character is found.

#include <iostream>
#include <string>

int main(){
  int select;
  std::string name;
  
  std::cout << "Select: ";
  std::cin >> select;

  std::cin.ignore(32767, '\n');

  std::cout << "Enter name: ";
  std::getline(std::cin, name);

  std::cout << "Hi " << name << "! You have selected " << select << std::endl;

  return 0;
}

A Program Handling All the Error Case in Input

Expand to see code
#include <iostream>

double getDouble(){
  double d;

  while(true){
    std::cout << "Enter: ";
    std::cin >> d;
    std::cin.ignore(32767, '\n'); /* clear '\n' from input stream */

    /*
     * Input will fail if a valid number isn't typed. 
     * if input fails, cin will set fail flag and stop extracting
     * characters from input stream.
     */
    if(std::cin.fail()){
      std::cout << "Please enter a floating number" << std::endl;
      std::cin.clear();       /* Clear fail flag */
      std::cin.ignore(32767, '\n'); /* Clear input stream */
    }
    else{
      return d;
    }
  }
}

char getOperator(){
  char op;

  while(true){
    std::cout << "Enter (+, -, * or /): ";
    std::cin >> op;
    std::cin.ignore(32767, '\n');
    
    if(op == '+' || op == '-' || op == '*' || op == '/'){
      return op;
    }
    else{
      std::cout << "Bad operator. Input again." << std::endl;
    }
  }
}

void printResult(double d1, char op, double d2){
  if(op == '+'){
    std::cout << d1 << " + " << d2 << " = " <<  d1 + d2 << std::endl;
  }
  else if(op == '-'){
    std::cout << d1 << " - " << d2 << " = " <<  d1 - d2 << std::endl;
  }
  else if(op == '*'){
    std::cout << d1 << " * " << d2 << " = " <<  d1 * d2 << std::endl;
  }
  else{
    if(d2 != 0){
      std::cout << d1 << " / " << d2 << " = " <<  d1 / d2 << std::endl;
    }
    else{
      std::cout << "Can't devide by zero!";
    }
  }
}

int main(){
  double d1 = getDouble();
  char op = getOperator();
  double d2 = getDouble();

  printResult(d1, op, d2);

  return 0;
}

Generating Random Numbers

Generating Random Numbers

#include <iostream>
#include <ctime>
#include <cstdlib>

int main(){
  /* For generating different seed each time the program runs */
  srand(static_cast<unsigned int>(time(0)));
  std::cout << rand();

  return 0;
}

Function for generating Random Numbers Between A Range

Using modulus:

#include <iostream>
#include <ctime>
#include <cstdlib>

int getRandom(int min, int max){
  return min + (rand() % (max - min + 1));
}

int main(){
  /* For generating different seed each time the program runs */
  srand(static_cast<unsigned int>(time(0)));
  std::cout << getRandom(1, 6);

  return 0;
}

But this method is biased to low numbers. Following method has less bias to low numbers.

#include <iostream>
#include <ctime>
#include <cstdlib>

int getRandom(int min, int max){
  static const double fraction = 1.0 / RAND_MAX;

  return min + ((max - min + 1) * (rand() * fraction));
}

int main(){
  /* For generating different seed each time the program runs */
  srand(static_cast<unsigned int>(time(0)));
  std::cout << getRandom(1, 6);

  return 0;
}

For details: http://www.learncpp.com/cpp-tutorial/59-random-number-generation/

Advanced Data Types

Array

Array Indexes

Array index must be a compile time constant.

#include <iostream>

int main(){
  int array[5]; // ok

  #define ARR_SIZE 5
  int array[ARR_SIZE];  // ok

  int const arr_size = 5;
  int array[arr_size];  // ok

  int arr_size = 5;
}

Representing Array Indexes with Enumerators

Array index can be represented with enumerators. In this way it makes arrays well documented:

#include <iostream>

namespace Store{
  enum Store{
    LM7805,
    MAX485,
    LM311,
    ATMEGA64,
    LED,
    MAX_ELEMENT
  };
}

int main(){
  int inStore[Store::MAX_ELEMENT];

  inStore[Store::LM7805] = 50;

  std::cout << "There are " << inStore[Store::LM7805] \
        << " LM7805 in store" << std::endl;

  return 0;
}

Relation Between Array and Pointer

Arrays are actually pointers. It points to the first element of the array.

#include <iostream>

int main(){
  int arr[2] = {1, 2};

  /* Following two address will be same*/
  std::cout << arr << std::endl;
  std::cout << &arr[0] << std::endl;

  return 0; 
}

Difference Between Array and Pointer to Array

The type of the array is int (*)[n] if it is an integer array but the type of the pointer to that array is int *. Array type contains the size information of the array. When array is dereferenced or assigned to a pointer it implicitly converts itself into type * from type (*)[n] so size information is lost. Here is an example of this behaviour:

#include <iostream>

int main(){
  int arr[5] = {1, 2, 3, 4, 5};
  int *arrPtr = arr;  // arr is converted from int (*)[2] to int *

  /* Will print the size of the array which is 5 * 8 bytes */
  std::cout << sizeof(arr) << std::endl;

  /* Will print the size of the pointer which is 8 bytes */
  std::cout << sizeof(arrPtr) << std::endl;

  return 0; 
}

String Constants Using Fixed Sized Array and Pointer

#include <iostream>

int main(){
  /*
   * keep the string constant in memory with r/w access
   * and return the pointer to it.
   * So string constant can be changed any time later
   */
  char arr[] = "hello, world";

  /*
   * Keep the string constant in read-only section of memory
   * so it can't be changed
   */
  char *text = "GNU is not Unix";

  /* As it is a constant so its better to initialize following way */
  const char *text = "GNU is not Unix";

  arr[0] = 'g';     // This OK

  /* 
   * In this case as string constant is kept in read-only
   * memory, doing this will generate segmentation
   * fault
   */
  *(text + 2) = 'O'; 

  std::cout << arr << std::endl;
  std::cout << text << std::endl;

  return 0; 
}

Pointers

Definig NULL Pointer C++ Way

From C++11 nullptr keyword can be used to define a NULL pointer.

#include <iostream>

int main(){
  int *ptr = nullptr;

  if(ptr){
    std::cout << "Not null" << std::endl;
  }
  else{
    std::cout << "Null" << std::endl;
  }
    
  return 0; 
}

Void Pointers

  • Can point to any data type
  • Have to cast manually to a data type before dereferencing.
  • Pointer arithmetic can’t be done using void pointers as size of the obect isn’t known
#include <iostream>

int main(){
  int x(5);
  void *ptr = &x; // pointing to an integer

  std::cout << *static_cast<int*>(ptr) << std::endl;

  char ch = 'P';  // pointing to a char
  ptr = &ch;
  
  std::cout << static_cast<char*>(ptr) << std::endl;

  return 0;
}

Converting A Pointer Address to Integer

Using reinterpret_cast<>:

#include <iostream>

int main(){
  int x = 17;
  unsigned int addressX = reinterpret_cast<int>(&x);

  std::cout << addressX << std::endl;

  return 0;
}

Dynamic Memory Allocation

There are three basic type of memory allocation:

  • Static memory allocation: Static and global variables. Allocated when program runs. Persist throught the program life. Memory is taken from the stack.

  • Atomatic memory allocation: Function parameter and local variables. Allocated when enter into relevent block and deallocated when exited the block. Memory is taken from the stack.

  • Dynamic memory allocation: Memory allocated and deallocated dynamically. Memory is taken from the heap.

Allocating Memory dynamically

Dynamically memory is allocated using the new keyword.

#include <iostream>

int main(){
  int *ptr = new int; // dynamically an integer is allocated.

  *ptr = 5;

  return 0;
}

Deallocating memory

Memory is deallocated using the delete keyword.

#include <iostream>

int main(){
  int *ptr = new int(5); // memory is allocated to the pointer

  /* memory is deallocated.
   * memory is realesed by os.
   */

  delete ptr; 

  /* still the pointer is holding the memory address
   * so its better to make it null
   */
  ptr = 0;
  ptr = nullptr;  // c++11

  return 0;
}

Memory leak

memory leak happens when allocated memory can’t be deallocated.

#include <iostream>

void doSomthing(){

  /* Memory is allocated but not deallocated
   * so memory leak happens when the variable
   * goes out of scope as there is no way
   * to deallocate in that case
   */

  int *ptr = new int;
}

int main(){
  doSomthing();
  return 0; 
}
#include <iostream>

int main(){
  int *ptr = int new;

  int val;

  // assigning to a address with out deallocating
  ptr = &val; // memory leak

  return 0;
}
#include <iostream>

int main(){
  int *ptr = new int(5);

  // allocating new memory without deallocating previous one
  int *ptr = new int(10);

  return 0;
}

Reference Variables

Create alias for a variable. Basically share same memory address. Must be initialized with an addressable object. Can be used in function to pass parameter by reference.

#include <iostream>

int main(){
  int x(10);
  int &y = x; // reference variable

  /*
   * will output:
   * 10
   * 10
   */
  std::cout << x << std::endl
  std::cout << y << std::endl

  return 0;
}

For Each Loop

  • Only works from c++11 above
  • Can’t be used in case of decayed arrays and dynamically allocated arrays.
#include <iostream>

int main(){
  int fibseq[] = {1, 1, 2, 3, 5, 8, 13, 21};

  std::cout << "Fibonacci Sequence: ";
  for(const auto &elem: fibseq){
    std::cout << elem << " ";
  }
  std::cout << std::endl;

  return 0;
}

Functions

Function Pointers

#include <iostream>

int foo(int x){
  return x*x;
}

int main(){
  int (*square)(int) = foo;

  std::cout << square(10) << std::endl;

  return 0;
}
  • Can’t be used for function’s with default arguments

Function Ellipsis

Ellipsis can be used to pass variable length argument in a function.

#include <iostream>
#include <cstdarg>  // to use ellipsis

void printNum(int count, ...){
  va_list list;
  va_start(list, count);

  for(int arg = 0; arg < count; arg++){
    std::cout << va_arg(list, int) << std::endl;
  }

  va_end(list);
}

int main(){
  printNum(5, 1, 2, 3, 4, 5);

  return 0;
}
  • Ellipsis are dangerous. Try to avoid them. For more – http://www.learncpp.com/cpp-tutorial/714-ellipsis-and-why-to-avoid-them/

Lambda Functions

To create anonymous functions. Simple example:

#include <iostream>
#include <algorithm>
#include <vector>

int main(int argc, char *argv[]) {
    std::vector<bool> bitvect{1, 0, 0, 1};

    /* lambda function example: used to make ~bitvect */
    std::transform(bitvect.begin(), bitvect.end(), bitvect.begin(),
            [](bool b){ return b == 1 ? 0 : 1; });
    
    return 0;
}

Syntaxt:

[&](){}: Capture all outside variable by reference. [=](){}: Capture all outside variable by value. [&x](){}: Capture only outside variable x by reference. [x](){}: Capture only outside variable x by value. [&, x](){}: Capture all outside variable by reference but x by value. []() -> Type {}: To specify return type.

Object Oriented Programming

Basic Class Example

#include <iostream>

class Point{

  private:
    double m_x;
    double m_y;

  public:
    double getX(){
      return m_x;
    }

    void setX(double x){
      m_x = x;
    }

    double getY(){
      return m_y;
    }

    void setY(double y){
      m_y = y;
    }
};

int main(){
  Point p;
  
  p.setX(-2.5);
  p.setY(2.5);

  std::cout << "(" << p.getX() << ", " << p.getY() << ")" << std::endl;

  return 0;
}

Constructors

#include <iostream>

class Point{

  private:
    double m_x;
    double m_y;

  public:
    Point(double x = 0, double y = 0): m_x(x), m_y(y){
        // empty
    }

    double getX(){
      return m_x;
    }

    void setX(double x){
      m_x = x;
    }

    double getY(){
      return m_y;
    }

    void setY(double y){
      m_y = y;
    }

    void printPoint(){
      std::cout << "(" << m_x << ", " << m_y << ")" << std::endl;
    }
};

int main(){
  Point p(0.5, 0.5);
  
  p.printPoint();

  return 0;
}

In c++11 its possible to give default value in class memeber variable declaration.

#include <iostream>

class Point{

  private:
    double m_x = 0; // default value of x
    double m_y = 0; // default value of y

  public:
    // member variable will be initialized with default value
    // if called
    Point(){
      //empty
    }

    double getX(){
      return m_x;
    }

    void setX(double x){
      m_x = x;
    }

    double getY(){
      return m_y;
    }

    void setY(double y){
      m_y = y;
    }

    void printPoint(){
      std::cout << "(" << m_x << ", " << m_y << ")" << std::endl;
    }
};

int main(){
  Point p;  // will call default constructor
  
  p.printPoint();

  return 0;
}

Destructors

#include <iostream>
#include <cassert>

class Point{

  private:
    double m_x;
    double m_y;

  public:
    Point(double x = 0, double y = 0): m_x(x), m_y(y){
      //empty
    }

    double getX(){
      return m_x;
    }

    void setX(double x){
      m_x = x;
    }

    double getY(){
      return m_y;
    }

    void setY(double y){
      m_y = y;
    }

    void printPoint(){
      std::cout << "(" << m_x << ", " << m_y << ")" << std::endl;
    }
};

class PointArray{

  private:
    Point *m_points;
    int m_length;

  public:
    PointArray(int length){
      m_points = new Point[length];
      m_length = length;
    }

    // Destructor
    ~PointArray(){
      delete[] m_points;
    }

    void insert(Point &p, int index){
      assert(index >= 0 && index < m_length);

      m_points[index] = p;
    }

    Point& get(int index){
      assert(index >= 0 && index < m_length);

      return m_points[index];
    }
};

int main(){
  PointArray parr(5);
  
  Point p(2, 3);
  Point q;

  parr.insert(p, 0);
  
  q = parr.get(0);
  q.printPoint();

  return 0;
}

Static Member Variables

  • Will shared by all object.
  • Not bound to any object. Bound to the whole class. So it is possible to use this variable without any object.
#include <iostream>

class Static{
  public:
    static int id;
};

/* Have to initialize first otherwise linker error will generate */
int Static::id = 1;

int main(){
  Static s;
  Static t;

  /* Shared by both object */
  s.id = 5;
  std::cout << s.id << "\t" << t.id << std::endl;

  /* Bound to whole class */
  Static::id = 10;
  std::cout << s.id << "\t" << t.id << std::endl;

  return 0;
}

Static Member Functions

  • Not bound to any object.
#include <iostream>

class ID{
  private:
    static int m_id;

  public:
    static int getID(){
      return m_id;
    }
};

/* Have to initialize first otherwise linker error will generate */
int ID::m_id = 1;

int main(){
  std::cout << ID::getID << std::endl;

  return 0;
}

Member Types

In C++ classes can have memeber types or nested types. They make the class easy to maintain. For example in the following example it will be easy to change the type from int to double. It need to change in only one line.

#include <iostream>

class Point{
public:
    using point_t = int; // Member type

    Point(point_t x, point_t y): m_x(x), m_y(y){}

    void print(void){
        std::cout << "(" << m_x << ", " << m_y << ")";
    }

private:
    point_t m_x;
    point_t m_y;
};

int main(int argc, char *argv[]){
    Point p(10, 20);
    p.print();
    return 0;
}

Access Specifiers

public: private: protected:

Chaining member functions

#include <iostream>

class Point{
public:
    using point_t = int;

    Point(point_t x, point_t y): m_x(x), m_y(y){}

    Point& add(point_t x, point_t y){
        this->m_x += x; 
        this->m_y += y;
        return *this;
    }

    Point& mul(point_t x, point_t y){
        this->m_x *= x; 
        this->m_y *= y;
        return *this;
    }

    void print(void){
        std::cout << "(" << m_x << ", " << m_y << ")";
    }

private:
    point_t m_x;
    point_t m_y;
};

int main(int argc, char *argv[]){
    Point p(10, 20);
    p.add(3, 5).mul(7, 8).add(2, 3);
    p.print();
    return 0;
}

Difference between structs and classes in C++

C++ structs and classes are same. Only difference is that all members in structs are public.

Friend function and friend class

Friend functions and classes can access the private members of a class. In the following example printWeather() is friend of both Humidity and Temperature class. So it can access private members of both classes.

#include <iostream>
 
class Humidity;
 
class Temperature
{
private:
    int m_temp;
public:
    Temperature(int temp=0) { m_temp = temp; }
 
    friend void printWeather(const Temperature &temperature, const Humidity &humidity);
};
 
class Humidity
{
private:
    int m_humidity;
public:
    Humidity(int humidity=0) { m_humidity = humidity; }
 
    friend void printWeather(const Temperature &temperature, const Humidity &humidity);
};
 
void printWeather(const Temperature &temperature, const Humidity &humidity)
{
    std::cout << "The temperature is " << temperature.m_temp <<
       " and the humidity is " << humidity.m_humidity << '\n';
}
 
int main()
{
    Humidity hum(10);
    Temperature temp(12);
 
    printWeather(temp, hum);
 
    return 0;
}

Classes can also be friend of another class. In following example Display class if a friend of Storage class so it can access the private members.

#include <iostream>
 
class Storage
{
private:
    int m_nValue;
    double m_dValue;
public:
    Storage(int nValue, double dValue)
    {
        m_nValue = nValue;
        m_dValue = dValue;
    }
 
    // Make the Display class a friend of Storage
    friend class Display;
};
 
class Display
{
private:
    bool m_displayIntFirst;
 
public:
    Display(bool displayIntFirst) { m_displayIntFirst = displayIntFirst; }
 
    void displayItem(const Storage &storage)
    {
        if (m_displayIntFirst)
            std::cout << storage.m_nValue << " " << storage.m_dValue << '\n';
        else // display double first
            std::cout << storage.m_dValue << " " << storage.m_nValue << '\n';
    }
};
 
int main()
{
    Storage storage(5, 6.7);
    Display display(false);
 
    display.displayItem(storage);
 
    return 0;
}

Operator Overloading

In C++ each operator is actually a function. For example the operator + is acturally operator+() function.

  • At least one of the operand will have to be a custom type

Using Friend Function

#include <iostream>

class Point{
private:
    using point_t = int;
    point_t m_x;
    point_t m_y;

public:
    Point(): m_x(0), m_y(0){}
    Point(point_t x, point_t y): m_x(x), m_y(y){}

    // constant memeber function
    void print(void) const {
        std::cout << "(" << m_x << ", " << m_y << ")" << "\n";
    }

    // This is not a member function only a friend function
    // This could defined outside of the function too
    friend Point operator+(cost Point &p, const int n){
        return Point(p.m_x + n, p.m_y + n); 
    }

    friend Point operator+(const int n, const Point &p){
        return p + n;
    }

    friend Point operator+(const Point &p1, const Point &p2){
        return Point(p1.m_x + p2.m_x, p1.m_y + p2.m_y);
    }
};

int main(int argc, char *argv[]){
    Point p1(10, 20);
    Point p2(4, 5);
    Point p3 = p1 + p2 + 5;

    p3.print();

    return 0;
}

Using Normal Function

If there is no need to access private class data, operator overloading can be done as normal function.

#include <iostream>

class Point{
private:
    using point_t = int;
    point_t m_x;
    point_t m_y;

public:
    Point(): m_x(0), m_y(0){}
    Point(point_t x, point_t y): m_x(x), m_y(y){}

    point_t getX(void) const {
        return m_x; 
    }
    
    point_t getY(void) const {
        return m_y; 
    }

    // constant memeber function
    void print(void) const {
        std::cout << "(" << m_x << ", " << m_y << ")" << "\n";
    }
};

Point operator+(const Point &p, const int n){
    return Point(p.getX() + n, p.getY() + n); 
}

Point operator+(int n, const Point &p){
    return p + n;
}

Point operator+(const Point &p1, const Point &p2){
    return Point(p1.getX() + p2.getX(), p1.getY() + p2.getY());
}

int main(int argc, char *argv[]){
    Point p1(10, 20);
    Point p2(4, 5);
    Point p3 = p1 + p2 + 5;

    p3.print();

    return 0;
}

Using Member Function

The assignment (=), subscript ([]), function call (()), and member selection (->) operators must be overloaded as member functions. IO operators(<< and >>) can’t be overloaded as memeber functions.

#include <iostream>

class Point{
private:
    using point_t = int;
    point_t m_x;
    point_t m_y;

public:
    Point(): m_x(0), m_y(0){}
    Point(point_t x, point_t y): m_x(x), m_y(y){}

    // No need to pass the class explicitly as it will be passed as hidden this pointer
    Point operator+ (int n){
        return Point(m_x + n, m_y +n);
    }
};

int main(int argc, char *argv[]){
    Point p1(10, 20);
    Point p2 = p1 + 5;
    return 0;
}

Overloading IO operators

#include <iostream>

class Point{
private:
    using point_t = int;
    point_t m_x;
    point_t m_y;

public:
    Point(): m_x(0), m_y(0){}
    Point(point_t x, point_t y): m_x(x), m_y(y){}

    point_t getX(void){
        return m_x; 
    }
    
    point_t getY(void){
        return m_y; 
    }

    friend std::ostream& operator<< (std::ostream &out, const Point &p);
    friend std::istream& operator>> (std::istream &in, Point &p);
};

// Overloading as friend function
// Returning std::ostream so that it can be chained like std::cout << p1 << p2
std::ostream& operator<< (std::ostream &out, const Point &p){
    out << "(" << p.m_x << ", " << p.m_y << ")";
    return out;
}

std::istream& operator>> (std::istream &in, Point &p){
    in >> p.m_x >> p.m_y;
    return in;
}

// Overloading as normal function
Point operator+ (Point p, int n){
    return Point(p.getX() + n, p.getY() + n); 
}

Point operator+ (int n, Point p){
    return p + n;
}

Point operator+ (Point p1, Point p2){
    return Point(p1.getX() + p2.getX(), p1.getY() + p2.getY());
}

int main(int argc, char *argv[]){
    Point p1(10, 20);
    Point p2;

    std::cout << "Enter a point:\n";
    std::cin >> p2;
    std::cout << p1 << " + " << p2 << " = " << p1 + p2 << "\n"; 

    return 0;
}

Functors

When classes acts like function calls they are called functors. This can be done by overloading () operator.

#include <iostream>

class Point{
private:
    using point_t = int;
    point_t m_x;
    point_t m_y;

public:
    Point(): m_x(0), m_y(0){}
    Point(point_t x, point_t y): m_x(x), m_y(y){}

    point_t getX(void){
        return m_x; 
    }
    
    point_t getY(void){
        return m_y; 
    }

    // () can only be overloaded as member function
    Point operator() (int n){
        return Point(m_x + n, m_y + n);
    }

    friend std::ostream& operator<< (std::ostream &out, const Point &p);
};

// Overloading as friend function
// Returning std::ostream so that it can be chained like std::cout << p1 << p2
std::ostream& operator<< (std::ostream &out, const Point &p){
    out << "(" << p.m_x << ", " << p.m_y << ")";
    return out;
}

int main(int argc, char *argv[]){
    Point pnt(10, 20);

    // Will add 5 to pnt.m_x, pnt.m_y and create new Point object.
    // Notce class object is called like function.
    std::cout << pnt(5) << "\n";

    return 0;
}

Overloading Typecast

Typecast overloading can be done to convert between types.

#include <iostream>
#include <cmath>

class Polar{
private:
    double m_r;
    double m_theta;

public:
    Polar(double r, double theta): m_r(r), m_theta(theta){}
    friend std::ostream& operator<< (std::ostream &out, const Polar &p);
};

// Overloading as friend function
// Returning std::ostream so that it can be chained like std::cout << p1 << p2
std::ostream& operator<< (std::ostream &out, const Polar &p){
    out << "(" << p.m_r << ", " << p.m_theta << ")";
    return out;
}

class Cartesian{
private:
    using point_t = int;
    point_t m_x;
    point_t m_y;

public:
    Cartesian(): m_x(0), m_y(0){}
    Cartesian(point_t x, point_t y): m_x(x), m_y(y){}

    friend std::ostream& operator<< (std::ostream &out, const Cartesian &c);

    // Typecast overloading
    operator Polar() const {
        double r = sqrt(pow(m_x, 2) + pow(m_y, 2)); 
        double theta = atan(static_cast<double>(m_y) / static_cast<double>(m_x));
        theta = (theta * 180) / M_PI;

        return Polar(r, theta);
    }
};

std::ostream& operator<< (std::ostream &out, const Cartesian &c){
    out << "(" << c.m_x << ", " << c.m_y << ")";
    return out;
}

int main(int argc, char *argv[]){
    Cartesian cart(10, 20);
    Polar pol(cart);

    std::cout << "Cartesian: " << cart << "\tPolar: " << pol << "\n";

    return 0;
}

Copy Constructors

When copy initialization is used a copy constructor is used. By default compiler uses memberwise initialization if no copy constructor is provided. For example:

class Xyz {
private:
    int m_var;
public:
    Xyz(int x): m_var(x){}
};

int main(void){
    Xyz xy(10);     // uses default initialization 
    
    Xyz yz(xy);     // Copy initialization
                    // As there is no copy constructor provided, compiler will do a memberwise initialization
}

Member functions of a class can access the private members ot the same class type. Here is an example with copy constructor:

class Xyz {
private:
    int m_var;
public:
    Xyz(int x): m_var(x){}

    // Copy constructor
    Xyz(const Xyz &xyz): m_var(xyz.m_var){}
};

int main(void){
    Xyz xy(10);     // uses default initialization 
    
    Xyz yz(xy);     // uses copy constructor
}

Elision

Copy initialization should be avoided as it can be elided for optimization.

#include <iostream>
#include <string>

class Hello{
    private:
        std::string m_s;

    public:
        Hello(std::string s): m_s(s){}

        Hello(const Hello &h): m_s(h.m_s){
            std::cout << "Copy constructor called\n";
        }

        std::string get(void){return m_s;}
};

int main(int argc, char *argv[]){
    Hello h("hello");
    Hello g(h);                 // Copy constructor will be used
    Hello i = Hello("gello");   // Copy constructor won't be used
    Hello k(Hello("gello"));    // Copy constructor won't be used

    std::cout << h.get() << g.get() << i.get() << k.get() << "\n";
}

If a class is passed by value in a function and return by value from a function copy constructor will be called. For example:

Hello changeHello(Hello h){ // Copy constructor will be called 
    h.change("new hello");
    return h;               // Copy constructor will be called
}

Implicit conversion, explicit and delete keyword

  • explicit keyword can be used to prevent implicit conversion.
  • delete keyword can be used to prevent both implicit and explicit conversion.
  • For details

Overloading assignment operator

  • Can only be oveloaded as memeber function.
  • Watch out for self assignment.
  • If no overloaded assignment operator is provided, compiler will do memberwise copy.
#include <iostream>
#include <string>

class Hello{
    private:
        std::string m_s;

    public:
        Hello(std::string s): m_s(s){}

        Hello(const Hello &h): m_s(h.m_s){
            std::cout << "Copy constructor called\n";
        }

        std::string get(void){return m_s;}

        Hello& operator= (const Hello &h){
            // If self copying
            if(this == &h) 
                return *this;   // for chainig

            m_s = h.m_s;

            return *this;   // for chaining
        }
};

int main(int argc, char *argv[]){
    Hello h("hello");
    Hello i("iello");
    Hello j("jello");
    Hello k(h);         // Copy constructor is called

    j = i = h;          // Overloaded function is called

    std::cout << h.get() << i.get() << j.get() << k.get() << "\n";
}

Shallow copy VS Deep copy

  • Shallow copy means memberwise copying by the compiler if no copy constructor or overloaded assingment operator is provided.
  • The default copy constructor and default assignment operators do shallow copies, which is fine for classes that contain no dynamically allocated variables.
  • Classes with dynamically allocated variables need to have a copy constructor and assignment operator that do a deep copy.

Object Relationship

Composition and Aggregation

In both cases relationship between parent and child is ‘has a’.

Composition

  • The part (member) is part of the object (class)

  • The part (member) can only belong to one object (class) at a time

  • The part (member) has its existence managed by the object (class)

  • The part (member) does not know about the existence of the object (class)

  • Typically use normal member variables

  • Can use pointer members if the class handles object allocation/deallocation itself

  • Responsible for creation/destruction of parts

Aggregation

  • The part (member) is part of the object (class)

  • The part (member) can belong to more than one object (class) at a time

  • The part (member) does not have its existence managed by the object (class)

  • The part (member) does not know about the existence of the object (class)

  • Typically use pointer or reference members that point to or reference objects that live outside the scope of the aggregate class

  • Not responsible for creating/destroying parts

Association

  • The associated object (member) is otherwise unrelated to the object (class)
  • The associated object (member) can belong to more than one object (class) at a time
  • The associated object (member) does not have its existence managed by the object (class)
  • The associated object (member) may or may not know about the existence of the object (class)

Dependencies

A dependency occurs when one object invokes another object’s functionality in order to accomplish some specific task. This is a weaker relationship than an association, but still, any change to object being depended upon may break functionality in the (dependent) caller. A dependency is always a unidirectional relationship.

Container Classes

Hold and organize multiple instance of another type(class or fundamental type).

std::initializer_list

Used in container class’s constructor for list initialization.

Inheritance

Order of construction

Most base class constructed first and most derived class constructed last.

#include <iostream>

class Parent{
public:
    Parent(){
        std::cout << "A" << "\n";
    }
};

class Child: public Parent{
public:
    Child(){
        std::cout << "B" << "\n";
    }
};

int main(int argc, char *argv[]){
    Child c;
    return 0;
}

/*
 * Will print:
 * A
 * B
 */

When a derived class is instanciated following things happen in order:

  • Memory for derived is set aside (enough for both the Base and Derived portions)
  • The appropriate Derived constructor is called
  • The Base object is constructed first using the appropriate Base constructor. If no base constructor is specified, the default constructor will be used.
  • The initialization list initializes variables
  • The body of the constructor executes
  • Control is returned to the caller

Constructors and initialization of derived classes

#include <iostream>

class Parent{
public:
    int m_x;
    
    Parent(int x=0): m_x(x){
        std::cout << "A" << "\n";
    }
};

class Child: public Parent{
public:
    int m_y;
    
    // Parent will be initialized with defautl vlaue
    Child(int y=0): m_y(y){
        std::cout << "B" << "\n";
    }

    // Parent will be initialized with given value
    Child(int x, int y): Parent(x), m_y(y){
        std::cout << "B" << "\n";
    }
};

int main(int argc, char *argv[]){
    // Parent's default constructor will be called
    Child c(10);

    // Parent will be initialized with given value
    Child d(20, 10);

    // Will print
    // Parent 0
    // Child 10
    std::cout << "Parent " << c.m_x << "\nChild " << c.m_y << "\n";
    
    // Will print
    // Parent 20
    // Child 10
    std::cout << "Parent " << d.m_x << "\nChild " << d.m_y << "\n";

    return 0;
}

When Child d(10, 20) is called following things happended:

  • Memory for Child is allocated.
  • The Child(int, int) constructor is called, where x = 10, and y = 20
  • The compiler looks to see if we’ve asked for a particular Base class constructor. We have! So it calls Parent(int) with x = 10.
  • The base class constructor initialization list sets m_x to 10
  • The base class constructor body executes, which prints A
  • The base class constructor returns
  • The derived class constructor initialization list sets m_y to 20
  • The derived class constructor body executes, which prints B
  • The derived class constructor returns

Order of Destruction

Destructors are called in reverse order of construction.

Inheritance and Access Specifiers

  • public: Can be accessed by anybody.
  • protected: Can be accessed by class member functions, friend functions and derived classes.
  • private: Can be accessed by only class member functions and friend functions.

A summary of the behavious when inherited publicly, protectedly or privately:

Access Specifier in Base Class Inherited Publicly Inherited Protectedly Inherited Privately
public public protected private
protected protected protected private
private inaccessible inaccessible inaccessible

Overriding Behavior

Redefining

#include <iostream>

class Base{
public:
    Base(){}

    void identify(void){
        std::cout << "I am base\n";
    }
};

class Derived: public Base{
public:
    Derived(){}

    void identify(void){
        std::cout << "I am derived\n";
    }
};

int main(int argc, char *argv[]){
    Derived d;

    // Will print I am derived
    d.identify();

    return 0;
}
  • Redefined functions doesn’t inherite access specification from parent.

Expanding Existing Functionality

#include <iostream>

class Base{
public:
    Base(){}

    void identify(void){
        std::cout << "I am base\n";
    }
};

class Derived: public Base{
public:
    Derived(){}

    void identify(void){
        // if scope isn't used Derived::identify() will be called
        Base::identify();
        std::cout << "I am derived\n";
    }
};

int main(int argc, char *argv[]){
    Derived d;

    // Will print
    // I am base
    // I am derived
    d.identify();

    return 0;
}

Changing an Inherited Member’s Access Specification

#include <iostream>

class Base{
public:
    Base(){}

protected:
    // Only membes, friends and derived class can call
    void identify(void){
        std::cout << "I am base\n";
    }
};

class Derived: public Base{
public:
    Derived(){}

    // Base::identify() is now public
    using Base::identify;
};

int main(int argc, char *argv[]){
    Base b;
    Derived d;

    // Error: can't call from here
    b.identify();

    // OK: as it is public in Derived class
    d.identify();

    return 0;
}

Functionality can be hidden by making it private in derived class.

#include <iostream>

class Base{
public:
    Base(){}

    void identify(void){
        std::cout << "I am base\n";
    }
};

class Derived: public Base{
public:
    Derived(){}

private:
    // Base::identify() is now private
    using Base::identify;
};

int main(int argc, char *argv[]){
    Base b;
    Derived d;

    // OK: as it is public in Base class.
    b.identify();

    // OK: as it is public in Derived class
    d.identify();

    return 0;
}

Multiple Inheritance

  • Ambiguity can arise.
#include <iostream>
 
class USBDevice
{
private:
    long m_id;
 
public:
    USBDevice(long id)
        : m_id(id)
    {
    }
 
    long getID() { return m_id; }
};
 
class NetworkDevice
{
private:
    long m_id;
 
public:
    NetworkDevice(long id)
        : m_id(id)
    {
    }
 
    long getID() { return m_id; }
};
 
class WirelessAdapter: public USBDevice, public NetworkDevice
{
public:
    WirelessAdapter(long usbId, long networkId)
        : USBDevice(usbId), NetworkDevice(networkId)
    {
    }
};
 
int main()
{
    WirelessAdapter c54G(5442, 181742);
    std::cout << c54G.getID(); // Which getID() do we call?

    // Can be solved using scope
    std::cout << c54G.USBDevice::getID();
 
    return 0;
}
  • Diamond problem

Virtual Functions

Pointers to the Base Class of Derived Object

It is possible to create pointers to the base class of derived object. But the pointers won’t be able to call member functions from the derived class.

#include <iostream>

class Base{
public:
    Base(){}

    void identify(void){
        std::cout << "I am base\n";
    }
};

class Derived: public Base{
public:
    Derived(){}

    void identify(void){
        std::cout << "I am derived\n";
    }
};

int main(int argc, char *argv[]){
    Derived *pd = new Derived();
    // Pointer to base of derived object
    Base *pb{pd};

    // Will call Derived::indentify()
    pd->identify();

    // Will call Base::identify()
    pb->identify();
    
    return 0;
}

Use case for this could be in functions which takes derived class as parameter. For each derived class a different functions have to be created. For example:

void report(Derived &d){
    d.identify();
}

void report(AnotherDerived &ad){
    ad.identify();
}

This problem can be solved using pointer/reference to base:

void report(Base &b){
    b.identify();
}

int main(int argc, char *argv[]){
    Derived d;
    AnotherDerived ad;

    report(d);
    report(ad);
}

But the problem is in both cases Base::identify() will be called. As pointer to base only can see memebers from base. This problem can be solved using virtual functions.

Polymorphism

A virtual function is a special type of function that, when called, resolves to the most-derived version of the function that exists between the base and derived class. This capability is known as polymorphism. A derived function is considered a match if it has the same signature (name, parameter types, and whether it is const) and return type as the base version of the function. Such functions are called overrides.

#include <iostream>

class Base{
public:
    Base(){}

    virtual void identify(void){
        std::cout << "I am base\n";
    }
};

class Derived: public Base{
public:
    Derived(){}

    void identify(void){
        std::cout << "I am derived\n";
    }
};

class AnotherDerived: public Derived{
public:
    AnotherDerived(){}

    void identify(void){
        std::cout << "I am another derived\n";
    }
};

int main(int argc, char *argv[]){
    AnotherDerived ad;
    Base *bp = &ad;

    // Both resolve to AnotherDerived::identify()
    ad.identify();
    bp->identify();

    Derived d;
    bp = &d;
    
    // Resolve to Derived::identity() as it is the most derived class in this case
    bp->identify();
        
    return 0;
}

Another example:

#include <iostream>
#include <string>

class Base{
public:
    Base(){}

    virtual std::string getName(void) const {return "Base";}
};

class Derived: public Base{
public:
    Derived(){}
    
    virtual std::string getName(void) const {return "Derived";}
};

class AnotherDerived: public Derived{
public:
    AnotherDerived(){}
    
    virtual std::string getName(void) const {return "AnotherDerived";}
};

void report(Base &b){
    std::cout << "I am " << b.getName() << "\n";
}

int main(int argc, char *argv[]){
    Derived d;
    AnotherDerived ad;
            
    report(d);
    report(ad);
    
    return 0;
}

If you want to call functions from base class in virtual function just use scope operator:

Derived d;
Base *bp = &d;

std::cout << bp->Base::getName() << "\n";
  • Return type have to match between virtual functions.
  • Never use virtual function in constructors and destructors.

override and final Specifiers

A derived virtual function is only considered override if functino signature and return type matches exactly between the virtual functions. override specifier enforces virtualization of a function. If signature and return type doesn’t match the compiler will generate an error. If override is used, no need to specify virtual keyword.

Covariant Return Type

Destructors

In case of inheritance destructors should always make virtual. Specially if there is dynamically allocated memory involved. If a derived class is converted to a base class and then call delete, only base destructor will be called. If dynamic memory is allocated in derived class, this will leak memory.

Abstract Class, Pure Virtual Functions and Interface Class

Abstract class is a class wich is only used by the derived class. It can’t be instantiated anywhere else.

A pure virtual function has no body at all. It is meant to be redefined in derived classes. A class with pure virtual functions is an abstract class means it can’t be intantiated. A pure virtual function may or may not have a body.

virtual void doSomething() = 0

In interface class has no member variables. All the functions are pure virtual.

Virtual Base Class

  • Single Base class is shared by the derived classes.
  • Solves diamond problem.
  • Most derived class is responsible for constructing the virtual base class.

Example:

#include <iostream>

class PoweredDevice
{
public:
    PoweredDevice(int power)
    {
    std::cout << "PoweredDevice: " << power << '\n';
    }
};

class Scanner: virtual public PoweredDevice // note: PoweredDevice is now a virtual base class
{
public:
    Scanner(int scanner, int power)
        : PoweredDevice{ power } // this line is required to create Scanner objects, but ignored in this case
    {
    std::cout << "Scanner: " << scanner << '\n';
    }
};

class Printer: virtual public PoweredDevice // note: PoweredDevice is now a virtual base class
{
public:
    Printer(int printer, int power)
        : PoweredDevice{ power } // this line is required to create Printer objects, but ignored in this case
    {
    std::cout << "Printer: " << printer << '\n';
    }
};

class Copier: public Scanner, public Printer
{
public:
    Copier(int scanner, int printer, int power)
        : PoweredDevice{ power }, // PoweredDevice is constructed here
        Scanner{ scanner, power }, Printer{ printer, power }
    {
    }
};

Object Slicing

Derived d;
Base b(d); // b will get the Base part from d. It is called object slicing

// Will call Base::doSomething() even if it is a virtual function
b.doSomething()

Base &b(d);

// Will call Derived::doSomething if it is a virtual function as b in reference to d
bp->doSomething()
  • In general avoid slicing

std::reference_wrapper

Dynamic Casting

Dynamic casing is used for downcasting.

Workaround for Friend Functions

Only member functions can be virtualized. So friend functions can’t be virtualized. For example:

#include <iostream>
class Base
{
public:
    Base() {}
 
    // Here's our overloaded operator<<
    friend std::ostream& operator<<(std::ostream &out, const Base &b)
    {
        // Delegate printing responsibility for printing to member function print()
        return b.print(out);
    }
 
    // We'll rely on member function print() to do the actual printing
    // Because print is a normal member function, it can be virtualized
    virtual std::ostream& print(std::ostream& out) const
    {
        out << "Base";
        return out;
    }
};
 
class Derived : public Base
{
public:
    Derived() {}
 
    // Here's our override print function to handle the Derived case
    virtual std::ostream& print(std::ostream& out) const override
    {
        out << "Derived";
        return out;
    }
};
 
int main()
{
    Base b;
    std::cout << b << '\n';
 
    Derived d;
    std::cout << d << '\n'; // note that this works even with no operator<< that explicitly handles Derived objects
 
    Base &bref = d;
    std::cout << bref << '\n';
 
    return 0;
}

Templates

Function Template

#include <iostream>

template <typename T>
T add(T x, T y){
    return x + y;
}

int main(int argc, char *argv[]){
    std::cout << add(1, 2) << "\n";
    std::cout << add(1.5, 2.1) << "\n";

    return 0;
}

For more than one type:

template <typename T1, typename T2>
void doSomething(T1 x, T1 y){

}

Class Template

#include <iostream>
#include <string>

template <class T>
class Value{
private:
    T m_x;

public:
    Value(T x): m_x(x){}

    T get(void){return m_x;}
};

int main(int argc, char *argv[]){
    Value<int> ival(10);
    Value<double> dval(10.25);
    Value<std::string> sval("hello");

    std::cout << "ival: " << ival.get() << " dval: " << dval.get() << " sval: " << sval.get()
        << "\n";
    return 0;
}

If template class definition and implementation are splitted into seperate files linker error can be generated. To solve this following can be done:

  • Definition and implementation in one file.
  • Implementation in *.inl file and #include in *.h file.

For more details

Template Non-Type parameters

#include <iostream>
 
template <class T, int size> // size is the non-type parameter
class StaticArray
{
private:
    // The non-type parameter controls the size of the array
    T m_array[size];
 
public:
    T* getArray();
    
    T& operator[](int index)
    {
        return m_array[index];
    }
};
 
// Showing how a function for a class with a non-type parameter is defined outside of the class
template <class T, int size>
T* StaticArray<T, size>::getArray()
{
    return m_array;
}
 
int main()
{
    // declare an integer array with room for 12 integers
    StaticArray<int, 12> intArray;
 
    // Fill it up in order, then print it backwards
    for (int count=0; count < 12; ++count)
        intArray[count] = count;
 
    for (int count=11; count >= 0; --count)
        std::cout << intArray[count] << " ";
    std::cout << '\n';
 
    // declare a double buffer with room for 4 doubles
    StaticArray<double, 4> doubleArray;
 
    for (int count=0; count < 4; ++count)
        doubleArray[count] = 4.4 + 0.1*count;
 
    for (int count=0; count < 4; ++count)
        std::cout << doubleArray[count] << ' ';
 
    return 0;
}

Template Specialization

If an exception is needed to make for an specific type.

#include <iostream>
#include <string>
#include <cstring>

char str[100];

template <class T>
T add(T x, T y){
    return x + y;
}

// If it is an const char*
template<>
const char *add(const char *s1, const char *s2){
    strcat(str, s1);
    strcat(str, s2);
    return str;
}

int main(int argc, char *argv[]){
    std::cout << add(1, 2) << "\n";
    std::cout << add(1.5, 2.2) << "\n";
    std::cout << add("hello", "world") << "\n";
    return 0;
}

Standard Template Library

Appendix A: Some Usefull Functions

  • decltype(s) – Query the type of s
C++ 讲义

讲义第一部分

  1. C++概述
  • 1.1 C++简介
  • 1.2 C++起源
  • 1.3 可移植性和标准
  • 1.4 为什么C++会成功
  1. C++初识
  • 2.1 简单的C++程序
    • 2.1.1 c++ hello world
    • 2.1.2 面向过程
    • 2.1.3 面向对象
    • 2.1.4 面向对象三大特性
  1. C++对C的扩展
  • 3.1 ::作用域运算符
  • 3.2 名字控制
  • 3.3 全局变量检测增强
  • 3.4 C++中所有的变量和函数都必须有类型
  • 3.5 更严格的类型转换
  • 3.6 struct类型加强
  • 3.7 “新增”bool类型关键字
  • 3.8 三目运算符功能增强
  • 3.9 C/C++中的const
    • 3.9.1 const概述
    • 3.9.2 C/C++中const的区别
      1. const概述
      2. C中的const
      3. 尽量以const替换#define
  • 3.10 引用(reference) 1. 引用基本用法 2. 函数中的引用 3. 引用的本质 4. 指针引用 5. 常量引用
  • 3.11 练习作业
  • 3.12 内联函数(inline function)
  • 3.13 函数的默认参数
  • 3.14 函数的占位参数
  • 3.15 函数重载(overload)
    • 3.15.1 函数重载概述
    • 3.15.2 函数重载
    • 3.15.3 extern “C”浅析
  1. 类和对象
    • 4.1 类和对象的基本概念
      • 4.1.1 C和C++中struct区别
        • c语言struct只有变量
        • c++语言struct 既有变量,也有函数
      • 4.1.2 类的封装
      • 4.1.3 将成员变量设置为private
      • 4.1.4 课堂练习
    • 4.2 面向对象程序设计案例
    • 4.3 对象的构造和析构
    • 4.4 C++面向对象模型初探
    • 4.5 友元
    • 4.6 运算符重载
    • 4.7 继承和派生
    • 4.8 多态

讲义第二部分

  1. 1.C++模板
  2. 2.C++类型转换
  3. C++异常
  4. c++输入和输出流

STL基础教程

  1. STL概论
  2. STL三大组件
  3. 常用容器
  4. 常用算法
  5. STL综合案例(学校演讲比赛)
C++简介
"c++"中的++来自于c语言中的递增运算符++,该运算符将变量加1。c++起初也叫"c with clsss".通过名称表明,c++是对C的扩展,因此c++是c语言的超集,这意味着任何有效的c程序都是有效的c++程序。c++程序可以使用已有的c程序库。

库是编程模块的集合,可以在程序中调用它们。库对很多常见的编程问题提供了可靠的解决方法,因此可以节省程序员大量的时间和工作量。

c++语言在c语言的基础上添加了面向对象编程和泛型编程的支持。c++继承了c语言高效,简洁,快速和可移植的传统。

c++融合了3种不同的编程方式:
  - c语言代表的过程性语言.
  - c++在c语言基础上添加的类代表的面向对象语言.
  - c++模板支持的泛型编程。
c语言和c++语言的关系:
c++语言是在C语言的基础上,添加了面向对象、模板等现代程序设计语言的特性而发展起来的。两者无论是从语法规则上,还是从运算符的数量和使用上,都非常相似,所以我们常常将这两门语言统称为"C/C++"。
C语言和C++并不是对立的竞争关系:
  - C++是C语言的加强,是一种更好的C语言。
  - C++是以C语言为基础的,并且完全兼容C语言的特性。
  
c语言和C++语言的学习是可以相互促进。学好C语言,可以为我们将来进一步地学习C++语言打好基础,而C++语言的学习,也会促进我们对于C语言的理解,从而更好地运用C语言。

#### C/C++全栈笔记
- 进程间通讯(IPC)方法
  - 管道(使用最简单)
  - 信号(开销最小)
  - 共享映射区(无血缘关系)
  - 本地套接字(最稳定)
- 管道
  管道是一种最基本的IPC机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:
  1. 其本质是一个伪文件(实为内核缓冲区) 
  2. 由两个文件描述符引用,一个表示读端,一个表示写端。
  3. 规定数据从管道的写端流入管道,从读端流出。

管道的原理: 管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。
管道的局限性:
1. 数据一旦被读走,便不在管道中存在,不可反复读取。
2. 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动。
3. 只能在有公共祖先的进程间使用管道。

常见的通信方式有,单工通信、半双工通信、全双工通信。
pipe函数
- 信号
- 共享映射区
- 本地套接字

- 网络编程-网络基础
- 协议的概念
- 网络应用程序设计模式
- C/S模式
传统的网络应用设计模式,客户机(client)/服务器(server)模式。需要在通讯两端各自部署客户机和服务器来完成数据通信。
- B/S模式
浏览器()/服务器(server)模式。只需在一端部署服务器,而另外一端使用每台PC都默认配置的浏览器即可完成数据的传输。
- 优缺点
对于C/S模式来说,其优点明显。客户端位于目标主机上可以保证性能,将数据缓存至客户端本地,从而提高数据传输效率。且,一般来说客户端和服务器程序由一个开发团队创作,所以他们之间所采用的协议相对灵活。可以在标准协议的基础上根据需求裁剪及定制。例如,腾讯公司所采用的通信协议,即为ftp协议的修改剪裁版。

因此,传统的网络应用程序及较大型的网络应用程序都首选C/S模式进行开发。如,知名的网络游戏魔兽世界。3D画面,数据量庞大,使用C/S模式可以提前在本地进行大量数据的缓存处理,从而提高观感。

C/S模式的缺点也较突出。由于客户端和服务器都需要有一个开发团队来完成开发。工作量将成倍提升,开发周期较长。另外,从用户角度出发,需要将客户端安插至用户主机上,对用户主机的安全性构成威胁。这也是很多用户不愿使用C/S模式应用程序的重要原因。

B/S模式相比C/S模式而言,由于它没有独立的客户端,使用标准浏览器作为客户端,其工作开发量较小。只需开发服务器端即可。另外由于其采用浏览器显示数据,因此移植性非常好,不受平台限制。如早期的偷菜游戏,在各个平台上都可以完美运行。

B/S模式的缺点也较明显。由于使用第三方浏览器,因此网络应用支持受限。另外,没有客户端放到对方主机上,缓存数据不尽如人意,从而传输数据量受到限制。应用的观感大打折扣。第三,必须与浏览器一样,采用标准http协议进行通信,协议选择不灵活。

因此在开发过程中,模式的选择由上述各自的特点决定。根据实际需求选择应用程序设计模式。
- 分层模型
- OSI七层模型
1. 物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后再转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。
2.  数据链路层:定义了如何让格式化数据以帧为单位进行传输,以及如何让控制对物理介质的访问。这一层通常还提供错误检测和纠正,以确保数据的可靠传输。如:串口通信中使用到的115200、8、N、1
3.  网络层:在位于不同地理位置的网络中的两个主机系统之间提供连接和路径选择。Internet的发展使得从世界各站点访问信息的用户数大大增加,而网络层正是管理这种连接的层。
4.  传输层:定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。 主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层数据叫做段。
5.  会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)。
6.  表示层:可确保一个系统的应用层所发送的信息可以被另一个系统的应用层读取。例如,PC程序与另一台计算机进行通信,其中一台计算机使用扩展二一十进制交换码(EBCDIC),而另一台则使用美国信息交换标准码(ASCII)来表示相同的字符。如有必要,表示层会通过使用一种通格式来实现多种数据格式之间的转换。
7.  应用层:是最靠近用户的OSI层。这一层为用户的应用程序(例如电子邮件、文件传输和终端仿真)提供网络服务。
- TCP/IP四层模型
TCP/IP网络协议栈分为应用层(Application)、传输层(Transport)、网络层(Network)和链路层(Link)四层。
- 协议格式
- TCP协议
- 网络名词术语解析


- SOCKET编程
Socket本身有“插座”的意思,在Linux环境下,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。

既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。与管道类似的,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。

套接字的内核实现较为复杂,不宜在学习初期深入学习。

在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程。“IP地址+端口号”就对应一个socket。欲建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socketpair就唯一标识一个连接。因此可以用Socket来描述网络连接的一对一关系。

在网络通信中,套接字一定是成对出现的。一端的发送缓冲区对应对端的接收缓冲区。我们使用同一个文件描述符索发送缓冲区和接收缓冲区。

TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API。本章的主要内容是socket API,主要介绍TCP协议的函数接口,最后介绍UDP协议和UNIX Domain Socket的函数接口。

网络字节序

TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。

IP地址转换函数

  #include <sys/socket.h>
  #include <netinet/in.h>
  #include <arpa/inet.h>
  int inet_aton(const char *cp, struct in_addr *inp);
  in_addr_t inet_addr(const char *cp);
  char *inet_ntoa(struct in_addr in);

  #include <arpa/inet.h>
  int inet_pton(int af, const char *src, void *dst);
  const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
网络套接字函数

  #include <sys/types.h> /* See NOTES */
  #include <sys/socket.h>
  int socket(int domain, int type, int protocol);
  domain:
    AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
    AF_INET6 与上面类似,不过是来用IPv6的地址
    AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用
  type:
    SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
    SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
    SOCK_SEQPACKET该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。
    SOCK_RAW socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
    SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序
  protocol:
    传0 表示使用默认协议。
  返回值:
    成功:返回指向新创建的socket的文件描述符,失败:返回-1,设置errno



#### 传智云盘项目
- 分布式存储FastDFS
- 缓存数据库redis
- 持久化数据库mysql
- HTTP协议
- Nginx
- FastCGI
- CGI
通用网关接口(Common Gateway Interface、CGI)描述了客户端和服务器程序之间传输数据的一种标准,可以让一个客户端,从网页浏览器向执行在网络服务器上的程序请求数据。  
CGI独立于任何语言的,CGI程序可以用任何脚本语言或者是完全独立编程语言实现,只要这个语言可以在这个系统上运行。Unix shell script、Python、 Ruby、PHP、 perl、Tcl、 C/C++和 Visual Basic 都可以用来编写 CGI 程序。

最初,CGI 是在 1993 年由美国国家超级电脑应用中心(NCSA)为 NCSA HTTPd Web 服务器开发的。这个 Web 服务器使用了 UNIX shell 环境变量来保存从 Web 服务器传递出去的参数,然后生成一个运行 CGI 的独立的进程。 

CGI处理流程

1. web服务器收到客户端(浏览器)的请求Http Request,启动CGI程序,并通过环境变量、标准输入传递数据
2. CGI进程启动解析器、加载配置(如业务相关配置)、连接其它服务器(如数据库服务器)、逻辑处理等
3. CGI进程将处理结果通过标准输出、标准错误,传递给web服务器
4. web服务器收到CGI返回的结果,构建Http Response返回给客户端,并杀死CGI进程

CGI使外部程序与Web服务器之间交互成为可能。CGI程序运行在独立的进程中,并对每个Web请求建立一个进程,这种方法非常容易实现,但效率很差,难以扩展。面对大量请求,进程的大量建立和消亡使操作系统性能大大下降。此外,由于地址空间无法共享,也限制了资源重用。

- FastCGI
快速通用网关接口(Fast Common Gateway Interface/FastCGI)是通用网关接口(CGI)的改进,描述了客户端和服务器程序之间传输数据的一种标准。

FastCGI致力于减少Web服务器与CGI程式之间互动的开销,从而使服务器可以同时处理更多的Web请求。与为每个请求创建一个新的进程不同,FastCGI使用持续的进程来处理一连串的请求。这些进程由FastCGI进程管理器管理,而不是web服务器。

FastCGI处理流程
1. Web 服务器启动时载入初始化FastCGI执行环境。 例如IIS、ISAPI、apache mod_fastcgi、nginx ngx_http_fastcgi_module、lighttpd mod_fastcgi。
2. FastCGI进程管理器自身初始化,启动多个CGI解释器进程并等待来自Web服务器的连接。启动FastCGI进程时,可以配置以ip和UNIX 域socket两种方式启动。
3. 当客户端请求到达Web 服务器时,Web服务器将请求采用socket方式转发FastCGI主进程,FastCGI主进程选择并连接到一个CGI解释器。Web 服务器将CGI环境变量和标准输入发送到FastCGI子进程。
4. FastCGI子进程完成处理后将标准输出和错误信息从同一socket连接返回Web服务器。当FastCGI子进程关闭连接时,请求便处理完成。
5. FastCGI子进程接着等待并处理来自Web 服务器的下一个连接。

由于FastCGI程序并不需要不断的产生新进程,可以大大降低服务器的压力并且产生较高的应用效率。它的速度效率最少要比CGI 技术提高 5 倍以上。它还支持分布式的部署,即FastCGI 程序可以在web 服务器以外的主机上执行。  
CGI 是所谓的短生存期应用程序,FastCGI 是所谓的长生存期应用程序。FastCGI像是一个常驻(long-live)型的CGI,它可以一直执行着,不会每次都要花费时间去fork一次(这是CGI最为人诟病的fork-and-execute 模式)。  
进程管理器管理:spawn-fcgi
Nginx不能像Apache那样直接执行外部可执行程序,但Nginx可以作为代理服务器,将请求转发给后端服务器,这也是Nginx的主要作用之一。其中Nginx就支持FastCGI代理,接收客户端的请求,然后将请求转发给后端FastCGI进程。
由于FastCGI进程由FastCGI进程管理器管理,而不是Nginx。这样就需要一个FastCGI进程管理器,管理我们编写FastCGI程序。

spawn-fcgi是一个通用的FastCGI进程管理器,简单小巧,原先是属于lighttpd的一部分,后来由于使用比较广泛,所以就迁移出来作为独立项目。

spawn-fcgi使用pre-fork 模型,功能主要是打开监听端口,绑定地址,然后fork-and-exec创建我们编写的FastCGI应用程序进程,退出完成工作。FastCGI应用程序初始化,然后进入死循环侦听socket的连接请求。 

编译安装spawn-fcgi
1. tar -zxvf spawn-fcgi-1.6.4.tar.gz
2. cd spawn-fcgi-1.6.4/
3. ./configure
4. make
5. sudo make install  

软件开发套件:fcgi  
编译安装fcgi  
1. tar -zxvf fcgi-2.4.1-SNAP-0910052249.tar.gz
2. cd fcgi-2.4.1-SNAP-0910052249/
3. ./configure
4. make
5. sudo make install

Nginx的fcgi的配置  





- FastDFS的Nginx模块
一个好的分布式文件系统最好提供Nginx的模块,因为对于互联网应用来说,像文件这种静态资源,一般是通过HTTP的下载,此时通过容易扩展的Nginx来访问FastDFS,能够让文件的上传和下载变得特别简单。  
Nginx安装FastDFS模块,主要是安装在FastDFS的存储服务器(storage)上,而不是tracker和client上。目的实际是为了,当输入地址(其中192.168.31.109 是一个storage服务器):http://192.168.31.109/group1/M00/00/00/wKgCbFem0l2ALSbFAAEYXfRAMkc536.png 
能够通过Nginx的Web服务功能,直接返回图片。


- QT客户端编程
- 后台数据处理
STL基础教程

STL基础教程

  1. STL概论
  2. STL三大组件
  3. 常用容器
    • string
    • vector
      • vect(T) v; vector(v.begin(), v.end()); vector(n,elem); vector(const vector & vec);
      • assign(beg, end); assign(n, elem); vector& operater=(const vector & vec); swap(vec);
      • size() empty() resize(int num) capacity() reserve(int len)
      • at(int idx); operater[]; front(); back();
      • insert(const_iterator pos, int count,ele); push_back(ele); pop_back(); erase(const_iterator start, const_iterator end); erase(const_iterator pos); clear();
    • deque
      • 构造函数
        • deque<T> deqT;//默认构造形式
        • deque(beg, end);//构造函数将[beg, end)区间中的元素拷贝给本身。
        • deque(n, elem);//构造函数将n个elem拷贝给本身。
        • deque(const deque &deq);//拷贝构造函数。
      • 赋值操作
        • assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
        • assign(n, elem);//将n个elem拷贝赋值给本身。
        • deque& operator=(const deque &deq); //重载等号操作符
        • swap(deq);// 将deq与本身的元素互换
      • 大小操作
        • deque.size();//返回容器中元素的个数
        • deque.empty();//判断容器是否为空
        • deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
        • deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除。
      • 双端插入和删除操作
        • push_back(elem);//在容器尾部添加一个数据
        • push_front(elem);//在容器头部插入一个数据
        • pop_back();//删除容器最后一个数据
        • pop_front();//删除容器第一个数据
      • 数据存取
        • at(idx);//返回索引idx所指的数据,如果idx越界,抛出out_of_range。
        • operator[];//返回索引idx所指的数据,如果idx越界,不抛出异常,直接出错。
        • front();//返回第一个数据。
        • back();//返回最后一个数据
      • 插入操作
        • insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置。
        • insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
        • insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
      • 删除操作
        • clear();//移除容器的所有数据
        • erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
        • erase(pos);//删除pos位置的数据,返回下一个数据的位置。
    • stack
    • queue
    • list
    • set/multiset
    • map/multimap
  4. 常用算法
  5. STL综合案例

您可能还喜欢...

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注