Cpp Essential

本文是在学习C++过程中所做的知识总结,是在阅读"Ivor_Horton’s_Beginning_Visual_Cpp_2012"和"Cpp_Primer_Plus"之余,记录的重点内容和个人知识盲区,以及与C语言相似特性的比较。方便忘记时,可以快速查阅。引用内容出处在此就不做一一标注。

1. Variable


1.1. Enumeration

enum (keyword)

/** Type-Safe Enumerations (C++ 11)
 *	Using type safe enumerations to avoid conflict of same enum name	
 */

// Old Enumerations
// ! Name conflict of [Diamonds]
enum Suit: char {Clubs, Diamonds, Hearts, Spades};
enum Jewels: char {Diamonds, Emeralds, Opals, Rubies, Sapphires};

// Type-Safe Enumerations (C++ 11)
// ! [Suit::Diamonds] and [Jewels::Diamonds] are different, no conflict
enum class Suit: char {Clubs, Diamonds, Hearts, Spades};
enum class Jewels: char {Diamonds, Emeralds, Opals, Rubies, Sapphires};
/** Type-Safe Enumerations (C++ 11)
 *  Demonstrating type-safe and non-type-safe enumerations
 */
#include <iostream>
#include <typeinfo>
using std::cout;
using std::endl;

// You can define enumerations at global scope
enum Jewels {Diamonds, Emeralds, Rubies};		// Uncomment this for an error
enum Suit : long {Clubs, Diamonds, Hearts, Spades};

int main()
{
	// Using the old enumeration type...
	Suit suit = Clubs;							// You can use enumerator names directly
	Suit another = Suit::Diamonds;	     		// or you can qualify them

	// Automatic conversion from enumeration type to integer
	cout << "suit value: " << suit << endl;
	cout << "Add 10 to another: " << another + 10 << endl;

	// Using type-safe enumerations...
	enum class Color : char {Red, Orange, Yellow, Green, Blue, Indigo, Violet};
	Color skyColor(Color::Blue);              	// You must qualify enumerator names
	// Color grassColor(Green);               	// Uncomment for an error

	// No auto conversion to numeric type
	cout << endl
		<< "Sky color data type: "           	// !!! skyColor is a new class named [color], not a char
        << typeid(skyColor).name() << endl;

	cout << endl
        << "Sky color value: "
        << static_cast<long>(skyColor) << endl;
   
	cout << skyColor + 10L << endl;				// Uncomment for an error
   
	cout << "Incremented sky color: "
        << static_cast<long>(skyColor) + 10L	// OK with explicit cast
        << endl;

	return 0;
}

1.2. Pointer & Reference

1.2.1. pointer

A pointer is a variable that stores the address of another variable of a particular type.

1.2.1.1. pointer to function

A pointer to function consists of following elements:

  1. the memory address of the function
  2. the parameter list for the function
  3. return type
// Prototype of a pointer to function:
return_type (*pointer_name)(list_of_parameter_types);
return_type *pointer_name(list_of_parameter_types);

// Function that return a pointer:
return_type* pointer_name(list_of_parameter_types);

// Arrays of Pointers to Functions:
return_type (*pointer_name[])(list_of_parameter_types);
return_type *pointer_name[](list_of_parameter_types);
/**	Pointer to function
 *	Pointer to a function & Function that return a pointer
 */

#include <iostream>
#include <cstring>

using namespace std;

double add(double a, double b)
{
    return a + b;
}

double multiply(double a, double b)
{
    return a * b;
}

double* p_add(double a, double b)
{
    double* result = new double;
    *result = a+b;
    return result;
}

int main()
{
    double* (*pfun1)(double, double);
    double (*pfun2)(double, double);
    double a=3.1416, b=1.5, *p=nullptr;

    pfun1=p_add;
    p=(pfun1(a,b));
    cout << *p << endl;
    delete p;

    pfun2 = add;
    cout << pfun2(a,b) << endl;

    pfun2 = multiply;
    cout << pfun2(a,b) << endl;

    return 0;
}
/**	Pointer to function
 *	A pointer to a function as an argument
 */
#include <iostream>

#define _countof(array) sizeof(array)/sizeof(array[0])

using namespace std;

// Function prototypes
double squared(double);
double cubed(double);
double sumarray(double data[], int len, double (*pfun)(double));

int main(void)
{
	double data[] = { 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5 };
	int len(_countof(data));
	
	cout << endl << "Sum of squares = " << sumarray(data, len, squared);
	cout << endl << "Sum of cubes = " << sumarray(data, len, cubed);
	cout << endl;
	
	return 0;
}
// Function for a square of a value
double squared(double x)
{
	return x*x;
}
// Function for a cube of a value
double cubed(double x)
{
	return x*x*x;
}
// Function to sum functions of array elements
double sumarray(double data[], int len, double (*pfun)(double))
{
	double total(0.0);				// Accumulate total in here
	
	for(int i = 0; i < len; i++)
		total += pfun(data[i]);
	
	return total;
}

1.2.2. reference

1.2.2.1. lvalue references
  1. An alias for a variable
  2. Refers to a persistent storage location that can appear on the left of an assignment operation
1.2.2.2. rvalue references
  1. An alias for a variable
  2. Reference an rvalue, which is a temporary value that is essentially transient
  3. Important in the context of functions

Difference between pointers & reference

TypeDifference
pointer1. Find the address of pointer >>> 2. get the variable’s address from pointer >>> 3. get the variable by the address
referenceGet the value directly by reference

Examples:

/**	Pointer & Reference:
 *	How declare
 */
int number(0);                                         // Initialized integer variable
int* pnumber(&number);                                 // Initialized pointer
char* proverb ("A miss is as good as a mile.");        // !!! actually an array of type const char, 
													   // in ISO C++, string can only be assigned to
													   // const char pointer
const char* proverb ("A miss is as good as a mile.");  // meaning is clear with const
int& rnumber(number);                                  // rnumber now is an alias of number, rnumber has a soild bind with number
/** Pointer & LValue reference
 *	A value can have different alias, these alias can be modifed with different modifier.
 */
#include <iostream>
	
using namespace std;
	
int main()
{
    int n=10;
    int* pn(&n);
    int& rn(n);
	const int& rcn(n);    // n can be changed by rn or n, but can't be changed by rcn
    int m=1024;

    cout << n << endl;
    cout << *pn << endl;
    cout << rn << endl;
	 cout << rcn << endl;

    pn=&m;                //pn has pointed to another variable m
    cout << n << endl;
    cout << *pn << endl;
    cout << rn << endl;
	cout << rcn << endl;
	
    rn=m;                 //rn just get the value of variable m, so n=1024
    cout << n << endl;
    cout << *pn << endl;
    cout << rn << endl;
	cout << rcn << endl;

	double temperatures[]={2.5,3.6,36.3,12.3};
	const double FtoC = 5.0/9.0;                 // Convert Fahrenheit to Centigrade
	for(auto& t : temperatures)                  // t is a reference
		t = (t - 32)*FtoC;                       // the variable that t reference to also changes
	for(auto& t : temperatures)
		cout << " " << t;
	cout << endl;

    return 0;
}
/**	RValue reference & LValue reference
 *	RValue is transient
 *	LValue is stored in a declared variable
 */
#include <iostream>

using namespace std;

int main()
{
    int x(5);
    int&& rExpr = 2*x + 3;  // rvalue reference
    int& rx(x);             // lvalue reference
    const int& lExpr = 2*x+3;
    cout << "x = 5, 2*x + 3 = "<< rExpr << endl;
    cout << "rx = " << rx << endl;
    cout << "lExpr = " << lExpr << endl;
    x=3;
    cout << "x = 3, 2*x + 3 = "<< rExpr << endl;
    cout << "rx = " << rx << endl;
    cout << "lExpr = " << lExpr << endl;

    return 0;
}
/**	rvalue reference parameter
 *	Keep in mind that this won’t be how rvalue references are intended to be used.
 *	Using an rvalue reference parameter
 *	The rvalue reference parameter itself is not an rvalue, it is an lvalue.
 */ 
#include <iostream>
using std::cout;
using std::endl;

int incr10(int &&num); // Function prototype

int main(void)
{
  int num(3);
  int value(6);
  int result(0);
  /*
   result = incr10(num);                              // Increment num
   cout << endl << "incr10(num) = " << result
        << endl << "num = " << num;

   result = incr10(value);                            // Increment value
   cout << endl << "incr10(value) = " << result
        << endl << "value = " << value;
*/
  result = incr10(value + num); // Increment an expression
  cout << endl
       << "incr10(value+num) = " << result
       << endl
       << "value = " << value;

  result = incr10(5); // Increment a literal
  cout << endl
       << "incr10(5) = " << result
       << endl
       << "5 = " << 5;

  cout << endl;
  return 0;
}

// Function to increment a variable by 10
int incr10(int &&num) // Function with rvalue reference argument
{
  cout << endl
       << "Value received = " << num;
  num += 10;
  return num; // Return the incremented value
}


1.3. Array

  • Array name is actually a const pointer, exactly read-only address.

1.3.1. Initialization of Array

/** Array initialization
 *	Demonstrating
 */
#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
   int value1[5] = { 1, 2, 3 };   //other initialized with 0
   int value2[5] { 1, 2, 3 };     //C++ 11 new character
   int junk [5];                  //no intialization

   cout << endl;
   for(int i = 0; i < 5; i++)
      cout << setw(12) << value1[i];

   cout << endl;
   for(int i = 0; i < 5; i++)
      cout << setw(12) << value2[i];

   cout << endl;
   for(int i = 0; i < 5; i++)
      cout << setw(12) << junk[i];

   cout << endl;
   return 0;
}

1.3.2. Array Traverse(Range-based for (C++11))

/** [Range-Based for]
 *  Demonstrating range-based for statement that gets array's value
 */
#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
	double temperatures[] = {65.5, 68.0, 75.0, 77.5, 76.4, 73.8,80.1};
	double sum = 0.0;
	int count = 0;

	for(double t : temperatures)
	{
		cout << setw(8) << t;
		sum += t;
		++count;
	}
	double average = sum/count;
	cout << endl << "Average:\t" << average << endl;

	sum = 0.0;
	count = 0;
    for(auto temperature : temperatures)
    {
        cout << setw(8) << temperature;
        sum += temperature;
        ++count;
    }
    average = sum/count;
    cout << endl << "Average:\t" << average << endl;

    return 0;
}

1.3.3. String

  • String is an array of characters.
  • C-style string: terminating ‘\0’
/** cin.getline(char *__s, std::streamsize __n, char __delim)
 *	Counting string characters
 */
#include <iostream>

using namespace std;

int main()
{
   const int MAX(80);                 // Maximum array dimension
   char buffer[MAX];                  // Input buffer
   int count(0);                      // Character count

   cout << "Enter a string of less than "
        << MAX << " characters:\n";
   cin.getline(buffer, MAX, '\n');    // Read a string until \n

   while(buffer[count] != '\0')       // Increment count as long as
      count++;                        // the current character is not null

   cout << endl
        << "The string \"" << buffer
        << "\" has " << count << " characters.";
   cout << endl;
   return 0;
}
/** DIY strtok()
 * 	split a string into words
 * 	There are two versions of strtok() made by myself:
 * 		const char *ustrtok(const char *str);
 * 		const char *ustrtok_split(const char *const str); 
 */
#include <iostream>
#include <cstring>

#define VNAME(v) (#v)
using namespace std;

const char *ustrtok(const char *str);
const char *ustrtok_split(const char *const str);
bool isws(const char c);

// Method 1: Split the string with '\0'
const char *ustrtok(const char *str)
{
	static const char *retWord = nullptr;	// word need to be returned
	static char *tempPtr = nullptr;			// copy of the string
	static size_t workPtr = 0;				// checking character's position 
	static size_t len = 0;					// string length

	// A new string pass in, make a copy of the new string
	if (str)
	{
		len = strlen(str);
		if (tempPtr != nullptr)
			delete tempPtr;
		tempPtr = new char[len + 1];
		workPtr = 0;
		strcpy(tempPtr, str);
	}

	// Parsing the string
	if (workPtr >= len) // Out of the stirng, have a cleaning
	{
		delete tempPtr;
		retWord = tempPtr = nullptr;
	}
	else // Fetch a word from the string
	{
		// Find the start of a word
		while (isws(*(tempPtr + workPtr)) && workPtr < len)
		{
			workPtr++;
		}
		retWord = ((*(tempPtr + workPtr)) ? (tempPtr + workPtr) : nullptr);

		// Find the end of a word
		while (!isws(*(tempPtr + workPtr)) && workPtr < len)
		{
			workPtr++;
		}
		if (workPtr < len)
		{
			*(tempPtr + workPtr) = '\0';
			workPtr++;
		}
	}

	return retWord;
}

// Method 2: Split the string into words
const char *ustrtok_split(const char *const str)
{
	static size_t wordStart = 0;
	static size_t len = 0;
	static const char *tempStr = nullptr;
	char *retWord = nullptr;

	// A new string pass in
	if (str)
	{
		tempStr = str;
		wordStart = 0;
		len = strlen(str);
	}

	// Parsing the string, fetch the word
	retWord = nullptr;
	if (wordStart < len) // !!!Not out of the string
	//if (*(tempStr + wordStart) !='\0') It's better to use length rather than '\0'
	{
		size_t wordLen = 0;
		// find the start of a word
		while (isws(*(tempStr + wordStart)))
			wordStart++;
		// Count the length of a word
		for (size_t i = 0; !isws(*(tempStr + wordStart + i)) && *(tempStr + wordStart + i) != '\0'; i++)
			wordLen++;
		if (wordLen > 0)
		{
			retWord = new char[wordLen + 1];
			for (size_t i = 0; i < wordLen; i++)
			{
				*(retWord + i) = *(tempStr + wordStart + i);
			}
			*(retWord + wordLen) = '\0';
		}
		wordStart += wordLen;
	}

	return retWord;
}

// if character c is a separator or ending
bool isws(const char c)
{
	return (strchr(" ,;\t\n", (int)c) && c != '\0') ? true : false;
}

// Test the function above
int main()
{
	const char *const str1 = "One Two     Three  ,, .....,., Four Five    ,,,;;;";
	const char *const str2 = "Hello again, I come here from far away!!!!!    ";
	const char *const str3 = "   ,;;;; ..... ...,,,.,. Hello again, I come here from far away!!!!!    ";
	const char *word = nullptr;

	cout << "Test for " << VNAME(str1) << endl;
	word = ustrtok(str1);
	while (word)
	{
		cout << word << endl;
		word = ustrtok(nullptr);
	};
	cout << endl;

	cout << "Test for " << VNAME(str2) << endl;
	word = ustrtok(str2);
	while (word)
	{
		cout << word << endl;
		word = ustrtok(nullptr);
	};
	cout << endl;

	cout << "Test for " << VNAME(str3) << endl;
	word = ustrtok(str3);
	while (word)
	{
		cout << word << endl;
		word = ustrtok(nullptr);
	};
	cout << endl;

	return 0;
}

1.3.4. Multidimensional Array

  • datatype data[3][4];

    datatype data[3][4]


1.4. LValues & RValues

1.4.1. LValue

  • an address in memory in which something is stored on an ongoing basis
  • can appear on the left of the equals sign in an assignment statement

1.4.2. RValue

  • the result(real value) of an expression that is stored transiently
  • can’t appear on the left of the equals sign in an assignment statement
int a(0), b(1), c(2);

a = b + c;					// b + c is stored temporarily in a memory location and the value is copied from this location to a

b = ++a;					// ++a is a LValue
(++a) = b;					// means: a=a++; a=b;

c = a++;					// a++ is a RValue
(a++) = c;					// Error

1.5. const

  • This is a type modifier that indicates that the variables are constants. Because you effectively tell the compiler that these are constants, the compiler will check for any statements that attempt to change the values of these variables, and if it finds any, it will generate an error message.
const Type* pstr;			// A pointer to a constant object
Type* const pstr;			// A constant pointer to an object
const Type* const pstr;		// A constant pointer to a constant object
const char* pstr;			// pstr should be pointed to a const String
/**	[const]
 *	Three types of the usage of modifier [const]
 */
#include <iostream>

int const_test() {
	char a='a', b='b';
	
	const char* pstr1 = &a;
	char* const pstr2 = &a;
	const char* const pstr3 = &a;
	
	pstr1=&b;
	//*pstr1=b;
	std::cout << *pstr1 << std::endl;
	
	*pstr2=b;
	//pstr2=&b;
	std::cout << *pstr2 << std::endl;

	//pstr3=&b;
	//*pstr3=b;
	std::cout << *pstr3 << std::endl;
	
	return 0;
}


1.6. Storage duration & Variable scope

Two types of scope

ScopeDuration
global scope / global namespace scope / file scopestatic variables
local scope / block scopestatic variables /automatic variables

Four types of storage duration

ScopeDuration
Automatic storage duration1. local scope or block scope: from the point at which it is declared until the end of the block containing its declaration
2. allocated automatically in a memory area called the stack
Static storage durationa variable that’s defined and accessible locally, but continues to exist after exiting the block in which it is declared
Dynamic storage durationallocate space dynamically at execution time, existing from the time you allocate it, until you free it
Thread Storage DurationThe storage duration of a thread-local object is the entire runtime of the thread for which it is created. Each thread has its own separate instance of a thread-local object, which is initialized when the thread starts.

Five storage class specifier

specifierobject
vs
function
info
autoobject1. automatic storage duration
2. permissible only in object declarations within a function
3. objects declared within a function have automatic storage duration by default
registerobject1. automatic storage duration
2. a hint to the compiler that the object should be made as quickly accessible as possible—ideally, by storing it in a CPU register
staticobject1. static storage duration
2. no linkage or internal linkage
3. internal linkage
staticfunction1. internal linkage
externobject & function1. static storage duration
2. external linkage, that means you can use them anywhere in the entire program
_Thread_localobject1. each thread has its own separate instance of the object

The Five Kinds of Variable Storage
The variable declared in a block is automatic duration by default.
The variable declared in a file is static duration by default.
The variable declared with modifier [extern] can be used in different files.

Storage DescriptionDurationScopeLinkageHow Declared
AutomaticAutomaticBlockNoneIn a block
RegisterAutomaticBlockNoneIn a block with the keyword register
Static with no linkageStaticBlockNoneIn a block with the keyword static
Static with external linkageStaticFileExternalOutside all functions
Static with internal linkageStaticFileInternalOutside all functions with the keyword static
/** Namespace
 *	Global scope
 *	scope resolution operator (::)
 */
#include <iostream>

using std::cout;
using std::endl;

int count1 = 100;                         // Global version of count1

int main()
{                                         // Function scope starts here
   int count1 = 10;
   int count3 = 50;
   cout << endl
        << "Value of outer count1 = " << count1
        << endl;
   cout << "Value of global count1 = " << ::count1            // From outer block
        << endl;

   {                                // New scope starts here...
      int count1 = 20;              // This hides the outer count1
      int count2 = 30;
      cout << "Value of inner count1 = " << count1
           << endl;
      cout << "Value of global count1 = " << ::count1         // From inner block
           << endl;

      count1 += 3;                  // This affects the inner count1
      count3 += count2;
   }                                // ...and ends here.

   cout << "Value of outer count1 = " << count1
        << endl
        << "Value of outer count3 = " << count3
        << endl;

   //cout << count2 << endl;        // uncomment to get an error
   
   return 0;
}  

1.6.1. Auto (keyword)

  • auto: define a variable with type deduced from the initial value you supply

Differences between the usage of [auto] in C &Cpp:

/* Cpp */
auto n = 16;				// Type is int
auto pi = 3.14159;			// Type is double
auto x = 3.5f; 				// Type is float
auto found = false; 		// Type is bool
/* C */
auto n = 16;				// Type is int
auto int n = 16;			// Type is int
auto pi = 3.14159;			// Type is int, pi=3
auto double pi = 3.14159;	// Type is double
/**	[auto]
 *	Use [auto] to declare a variable's type by its initialized value
 */
#include <iostream>
#include <typeinfo>
using namespace std;

int auto_test_in_Cpp()
{
    auto n = 16;        // Type is int
    auto pi = 3.14159;  // Type is double
    auto x = 3.5f;      // Type is float
    auto found = false; // Type is bool

    cout << "n\t" << n << "\t" << typeid(n).name() << endl;
    cout << "pi\t" << pi << "\t" << typeid(pi).name() << endl;
    cout << "x\t" << x << "\t" << typeid(x).name() << endl;
    cout << "found\t"  << found<< "\t" << typeid(found).name() << endl;

    return 0;
}

/**	[auto] in C & C++
 *	[auto] means [auto int] in C
 */
#include <stdio.h>
#include <stdlib.h>

#define false 0

int auto_test_in_C()
{
    auto n = 16;        // Type is int
    auto pi = 3.14159;  // Type is double
    auto x = 3.5f;      // Type is float
    auto found = false; // Type is bool

    printf("%d \t %d\n", sizeof(n),n);
    printf("%d \t %d\n", sizeof(pi),pi);
    printf("%d \t %d\n", sizeof(x),x);
    printf("%d \t %d\n", sizeof(found),found);

    return 0;
}
/**	trailing return type
 *	use [auto] to declare the retrun type of a function
 */
template<class T> auto max(T x[], const int& len) -> T
{
	T maximum(x[0]);
	for(int i = 1; i < len; i++)
		if(maximum < x[i])
			maximum = x[i];
	return maximum;
}
/**	trailing return type
 *	use [auto]&[decltype] to declare the retrun type of a function
 */
#include <iostream>
using std::cout;
using std::endl;
template<class T1, class T2>
auto product(T1 v1[], T2 v2[], const size_t& count) -> decltype(v1[0]*v2[0])
{
	decltype(v1[0]*v2[0]) sum(0);
	for(size_t i = 0; i<count; i++) sum += v1[i]*v2[i];
	
	return sum;
}
int main()
{
	double x[] = {100.5, 99.5, 88.7, 77.8};
	short y[] = {3, 4, 5, 6};
	long z[] = {11L, 12L, 13L, 14L};
	size_t n = 4;
	
	cout << "Result type is " << typeid(product(x, y, n)).name() << endl;
	cout << "Result is " << product(x, y, n) << endl;
	
	auto result = product(z, y, n);
	cout << "Result type is " << typeid(result).name() << endl;
	cout << "Result is " << result << endl;
	
	return 0;
}

1.6.2. Static (keyword)

  • Static Variables in a function
    • a variable whose value persists from one call of a function to the
      next
    • Initialization of a static variable within a function only occurs the
      first time that the function is called.
/**	[static]	modifier
 *  Using a static variable within a function
 */
// 
#include <iostream>
using std::cout;
using std::endl;
        
void record(void);      // Function prototype, no arguments or return value
        
int main(void)
{
   record();
        
   for(int i = 0; i <= 3; i++)
      record();
        
   cout << endl;
   return 0;
}
        
// A function that records how often it is called
void record(void)
{
   static int count(0);
   cout << endl << "This is the " << ++count;
   if((count > 3) && (count < 21))         // All this....
      cout <<"th";
   else
      switch(count%10)                     // is just to get...
      {
         case 1: cout << "st";
                 break;
         case 2: cout << "nd";
                 break;
         case 3: cout << "rd";
                 break;
         default: cout << "th";      // the right ending for...
      }                              // 1st, 2nd, 3rd, 4th, etc.
   cout << " time I have been called";
   return;
}

2. Operator


2.1. Precedence

PRECEDENCEOPERATORSASSOCIATIVITY
1::None
2()
[]
->
.
Left
2postfix ++
postfix –
Left
2typeidLeft
2const_cast
dynamic_cast
static_cast
reinterpret_cast
Left
3logical not !
one’s complement ~
Right
3unary +
unary -
Right
3prefix ++
prefix –
Right
3address-of &
indirection *
Right
3type cast (type)Right
3sizeof
decltype
Right
3new
delete
Right
4.*Left
4->*Left
5*     /     %Left
6+     -Left
7<<     >>Left
8==     !=Left
9&Left
10^Left
11|Left
12&&Left
13||Left
14?: (conditional operator)Right
15=
*=     /=     %=     +=     -=     &=     ^=     |=
<<=     >>=
Right
16ThrowRight
17,Left

2.2. typeid

cout << "The type of x*y is " << typeid(x*y).name() << endl;

2.3. sizeof

The sizeof operator produces an integer value of type size_t(usually(typedef size_t unsigned int)) that gives the number of bytes occupied by its operand.

//Use sizeof to count element number in an array
#define _countof(array) sizeof(array)/sizeof(*array)

2.4. decltype

decltype: obtain the type of an expression, so decltype(exp) is the type of the value that results from evaluating the expression exp.

/**	decltype:	obtain the type of an expression
 *	The primary use for the decltype operator is in defining function templates.
 */

//	This may make no sense, it's just a demonstration
template<class T1, class T2>
return_type f(T1 v1[], T2 v2[], const size_t& count)
{
	// The type of sum depands on the parameters' type defined by function template
	decltype(v1[0]*v2[0]) sum(0);
	for(size_t i = 0; i<count; i++) sum += v1[i]*v2[i];
	return sum;
}

//	!!!Error	
template<class T1, class T2>
decltype(v1[0]*v2[0]) f(T1 v1[], T2 v2[], size_t count) // Will not compile!
{
	decltype(v1[0]*v2[0]) sum(0);
	for(size_t i = 0; i<count; i++) sum += v1[i]*v2[i];
	return sum;
}

//	[auto] & [decltype]
template<class T1, class T2>
auto f(T1 v1[], T2 v2[], const size_t& count) -> decltype(v1[0]*v2[0])	//	trailing return type
{
	decltype(v1[0]*v2[0]) sum(0);
	for(size_t i = 0; i<count; i++) sum += v1[i]*v2[i];
	return sum;
}

2.5. cast

cast (explicit type conversion): To avoid some exceptions caused by implicit type conversion, you need to cast some variable in a expression with mixed type.

TypeConversion TimeHow Declared
static_castthe conversion is checked statically
(when program is compiled)
static_cast<the_type_to_convert_to>(expression)
dynamic_castthe conversion is checked dynamically
(when program is executing)
dynamic_cast<the_type_to_convert_to>(expression)
const_castremoving the const-ness of an expressionconst_cast<the_type_to_convert_to>(expression)
reinterpret_castunconditional castreinterpret_cast<the_type_to_convert_to>(expression)
old-style conversioncover all cast mentioned above(the_type_to_convert_to)[(]expression[)]
/**	[static_cast<>()]	operator
 *	Explicit type conversion
 */

#include <iostream>

using namespace std;

int main()
{
    double value1 = 10.5;
    double value2 = 15.5;
    char a=-1;
    char b=10;
    int sum;

    sum=value1+value2;
    cout << sum << endl;

    sum=static_cast<int>(value1)+static_cast<int>(value2);
    cout << sum << endl;

    sum=(int)value1+(int)value2;
    cout << sum << endl;

    sum=a+b;
    cout << sum << endl;

    sum=static_cast<unsigned char>(a)+static_cast<unsigned char>(b);
    cout << sum << endl;

    sum=(unsigned char)a+(unsigned char)b;
    cout << sum << endl;

    return 0;
}

2.6. new & delete

Dynamic Memory Allocation: memory be allocated for storing different types of variables at execution time, so dynamically allocated variables can’t have been defined at compile time.
This unused memory is called the heap in C++, or sometimes the free store.
Process virtual address space

/** Use operator [new] & [delete] to allocate memory dynamically.
 *  
 * 	new && delete (operator)
 *	new:			allocate memory
 *	delete:			de-allocate memory
*/

/*	1. Variable	*/
double* pvalue = new double(999.0);
//do something with the new value
delete pvalue;

/*	2. Array	*/
double* pvalue = new double[20];
delete [] pvalue;

/*	3. Multidimensional Arrays	*/
//Allocate memory for a 3x4 array
double (*pbeans)[4](nullptr);
pbeans = new double [3][4];
//release memory
delete [] pbeans;

//Use auto to simplify the declaration
auto pbeans = new double [3][4]

//Allocate memory for a mx10x10 array
//only the leftmost dimension may be specified by a variable
int m;
cin >> m;
auto pBigArray = new double [m][10][10];
//release memory
delete [] pBigArray;
/**	Dynamic memory allocation
 *	Calculating primes using dynamic memory allocation
 */
// 
#include <iostream>
#include <iomanip>
using namespace std;
        
int main()
{
	long* pprime(nullptr);         				// !!! Pointer to prime array
	
	long trial(5);                				// Candidate prime
	int count(3);                  				// Count of primes found
	bool found(false);             				// Indicates when a prime is found
	int max(0);                    				// Number of primes required
	     
	cout << endl
	     << "Enter the number of primes you would like (at least 4): ";
	cin >> max;                    				// Number of primes required
	     
	if(max < 4)  			    	            // Test the user input, if less than 4
	   max = 4;              	 			    // ensure it is at least 4
	     
	pprime = new long[max];				        // !!! dynamic memory allocation with size of max
	     
	*pprime = 2;                   				// Insert three
	*(pprime + 1) = 3;             				// seed primes
	*(pprime + 2) = 5;
	     
	do
	{
	   trial += 2;                           	// Next value for checking
	   found = false;                          	// Set found indicator
	     
	   for(int i = 0; i < count; i++)          	// Division by existing primes
	   {
	      found =(trial % *(pprime + i)) == 0; 	// True for exact division
	      if(found)                            	// If division is exact
	         break;                            	// it's not a prime
	   }
	     
	   if (!found)                             	// We got one...
	      *(pprime + count++) = trial;         	// ...so save it in primes array
	} while(count < max);
	     
	// Output primes 5 to a line
	for(int i = 0; i < max; i++)
	{
	   if(i % 5 == 0)                          	// New line on 1st, and every 5th line
	      cout << endl;
	   cout << setw(10) << *(pprime + i);
	}
	     
	delete [] pprime;                          	// !!! Free up memory
	pprime = nullptr;                          	// and reset the pointer
	cout << endl;
	
	return 0;
}

3. Statement


3.1. for

// How to use [for]
for(initializing_expression; text_expression; increment_expression)
{
	loop_statement;
}
next_statement;

for

/** [for] statement
 *	Display ASCII codes for alphabetic characters
 */

#include <iostream>
#include <iomanip>

using namespace std;
int main()
{
   //using char as counter
   for(char capital = 'A', small = 'a'; capital <= 'Z'; capital++, small++)
   {
      cout << endl
           << "\t" << setw(10) << setiosflags(ios::left)<< capital                     // Output capital as a character
           << hex << setw(10) << setiosflags(ios::left) << static_cast<int>(capital)   // and as hexadecimal
           << dec << setw(10) << setiosflags(ios::left) << static_cast<int>(capital)   // and as decimal
           << "\t"<< setw(10) << setiosflags(ios::left) << small                       // Output small as a character
           << hex << setw(10) << setiosflags(ios::left) << static_cast<int>(small)     // and as hexadecimal
           << dec << setw(10) << setiosflags(ios::left) << static_cast<int>(small);    // and as decimal
   }
   cout << endl;

   //using double as counter
   double a(0.3), b(2.5);
   for(double x = 0.0; x <= 2.0; x += 0.25)
      cout << "\n\tx = " << x
	       << "\ta*x + b = " << a*x + b;

   return 0;
}
/**	Range-based for
 *	use a block ranged variable to fetch elements in array
 */
double temperatures[] = {65.5, 68.0, 75.0, 77.5, 76.4, 73.8,80.1};
double sum = 0.0;
int count = 0;
//	t fetch elements in temperatures[]
for(double t : temperatures)
{
	sum += t;
	++count;
}
double average = sum/count;

//	if you want to change elements in array, use an alias
const double FtoC = 5.0/9.0;                 // Convert Fahrenheit to Centigrade
for(auto& t : temperatures)                  // t is a reference
	t = (t - 32)*FtoC;                       // the variable that t reference to also changes

3.2. while & do-while

  • do-while
do
{

	//	loop_statements;

}while(condition);

do-while

  • while
while(condition){
	loop_statement;
}

在这里插入图片描述


3.3. continue & break

  • continue: jump to the start of a loop and continue the next loop
  • break: jump out of the loop and continue the next statement out of the loop

3.4. switch-case

/**	Case-range variable
 *	
 *	It will lead to error when a variable is declared in a case without bracket.
 *	Because a variable declared in a case without bracket has a scope of the whole switch-case block.
 *	But program can only go to one case.
 *	Initialization of the variable may be skipped by 'default' label.
*/

/*
    switch (choice)
    {
    case 1:
    int count = 2;
    cout << "Boil " << count
          << " eggs." << endl;
    // Code to do something with count...
    break;
    default:
    cout << endl
          << "You entered a wrong number, try raw eggs." << endl;
    break;
    }
*/

switch(choice)
{
	case 1:
	{
		int count = 2;           //	!!! block-range variable [count]
		cout << "Boil " << count
			<< " eggs." << endl;
		// Code to do something with count...
		break;
	}
	default:
		cout << endl <<"You entered a wrong number, try raw eggs." << endl;
		break;
}
/**	Multiple case actions
 *	Sharing a case
*/

#include <iostream>

using namespace std;

int main()
{
   char letter(0);
   cout << endl
        << "Enter a small letter: ";
   cin >> letter;

   switch(letter*(letter >= 'a' && letter <= 'z'))
   {
	  // all the vowel letter sharing the same action
      case 'a': case 'e': case 'i': case 'o': case 'u':
        cout << endl << "You entered a vowel.";
        break;

      case 0:
		cout << endl << "That is not a small letter.";
        break;

      default: cout << endl << "You entered a consonant.";
   }
   cout << endl;
   return 0;
}

3.5. goto

Whenever possible, you should avoid using gotos in your program.
There are occasions when it can be convenient by goto, such as when you must exit from a deeply nested set of loops.

/**	Some proper usage of [goto]
 *	When many branches go to the same handle, using [goto] can be convenient.
 */
HRESULT HrDoSomething(int parameter)
{
	//parameter check and initialization
	//processing part1
	if (SomeCode(a) != ok)
	{
		//Set HR value
		goto Error;
	}
	//processing part1
	if (SomeCode(b) != ok)
	{
		//Set HR value
		goto Error;
	}
Error:
	//clean up
	return hr;  // Modify the code here, without change in If block
}

// Exit the nested loop immediately
HRESULT HrDoSomething(int parameter)
{
	//parameter check and initialization
	//processing part1
	for(int i=0; i<100; i++)
	{
		// do something
		for(int j=0; j<100; j++)
		{
			// do something
			for(int k=0; k<100; k++){
				// do something
				if(i+j+k > 500)
					// Exit the nested loop immediately
					// break can only pass to the next statement following the loop
					goto EndNestedLoop;
			}
			// do something		
		}
		// do something
	}
EndNestedLoop:
	//continue to do something else
	
	return hr;  // Modify the code here, without change in If block
}

4. Structure


4.1. namespace

Namespaces provide a way to separate the names used in one part of a program from those used in another. This is invaluable with large projects involving several teams of programmers working on different parts of the program. Each team can have its own namespace name, and worries about two teams accidentally using the same name for different functions disappear.

/**	Declaring a namespace 
 *	This is similar to declaration of struct
 */
#include <iostream>

using namespace std;

namespace myStuff
{
  int value = 0;
}

int main()
{
  cout << "enter an integer: ";
  cin  >> myStuff::value;
  cout << "\nYou entered " << myStuff::value << endl;
  return 0;
}


4.2. function

4.2.1. The General Form of a Function

return_type function_name(parameter_list)
{
	//declare some variables;
	//do something;
	//return expression;
}

4.2.2. Pass-Value Mechanism

4.2.2.1. The Pass-by-Value Mechanism

The Pass-by-Value Mechanism

4.2.2.1.1. Pointers as Arguments to a Function

A pointer is an address of another variable, and if you take a copy of this address, the copy still points to the same variable

4.2.2.1.2. Passing Arrays to a Function

The array is not copied. The array name is converted to a pointer, and a copy of the pointer to the beginning of the array is passed by value to the function.

/**	Pass array as parameter
 *	Handling an array in a function as a pointer
 */
#include <iostream>
using namespace std;

double average(double* array, int count);	//Function prototype
/*
double average(double array[], int count);	//Also right
*/
int main(void)
{
   double values[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 };

   cout << endl << "Average = "
        << average(values, (sizeof values)/(sizeof values[0])) << endl;
   return 0;
}

// Function to compute an average
double average(double* array, int count)
{
   double sum(0.0);                    // Accumulate total in here
   for(int i = 0; i < count; i++)
      sum += *array++;                 // Sum array elements
	  // sum += array[i];              // Also right

   return sum/count;                   // Return average
}
4.2.2.1.3. Passing Multidimensional Arrays to a Function
// Passing a two-dimensional array to a function
#include <iostream>

using namespace std;

double yield(double beans[][4], int n);
/*
double yield(double (*beans)[4], int count)    // Also Right
*/

int main(void)
{
   double beans[3][4] =  {   { 1.0,  2.0,  3.0,  4.0 },
                             { 5.0,  6.0,  7.0,  8.0 },
                             { 9.0, 10.0, 11.0, 12.0 }   };

   cout << endl << "Yield = " << yield(beans, sizeof beans/sizeof beans[0])
        << endl;
   return 0;
}

// Function to compute total yield
double yield(double beans[][4], int count)
{
   double sum(0.0);
   for(int i = 0; i < count; i++)      // Loop through number of rows
      for(int j = 0; j < 4; j++)       // Loop through elements in a row
         sum += beans[i][j];
   return sum;
}
4.2.2.2. The Pass-by-Reference Mechanism (can’t be used in C)
  • The parameter acts as an alias for the argument passed. This eliminates any copying of the argument supplied and allows the function to access the caller argument directly.
    No value copying, no de-reference.
  • A reference as a function parameter is created and initialized each time the function is called, and is destroyed when the function ends, so you get a completely new reference created each time you use the function.
  • Passing arrays to a function is pass-by-reference way, but it actually pass a pointer to the function.
  • The rvalue reference parameter itself is not an rvalue, it is an lvalue that can only be assigned by rvalue.
// Using an lvalue reference to modify caller arguments
#include <iostream>

using namespace std;

int incr10(int& num);                // Function prototype

int main(void)
{
   int num(3);
   int value(6);

   int result = incr10(num);
   cout << endl  << "incr10(num) = " << result
        << endl << "num = " << num;

   result = incr10(value);
   cout << endl << "incr10(value) = " << result
        << endl << "value = " << value << endl;
   return 0;
}

// Function to increment a variable by 10
int incr10(int& num)                 // Function with reference argument
{
   cout << endl << "Value received = " << num;

   num += 10;                        // Increment the caller argument
                                     //  - confidently
   return num;                       // Return the incremented value
}
// Using an rvalue reference parameter

#include <iostream>
using namespace std;

int incr10(int&& num);              // Function prototype

int main(void)
{
   int num(3);      
   int value(6);
   int result(0);
/*
   result = incr10(num);                              // Increment num
   cout << endl << "incr10(num) = " << result
        << endl << "num = " << num;

   result = incr10(value);                            // Increment value
   cout << endl << "incr10(value) = " << result
        << endl << "value = " << value;
*/
   result = incr10(value+num);                        // Increment an expression
   cout << endl << "incr10(value+num) = " << result
        << endl << "value = " << value;

   result = incr10(5);                                // Increment a literal
   cout << endl << "incr10(5) = " << result
        << endl << "5 = " << 5;

   cout << endl;
   return 0;
}

// Function to increment a variable by 10
int incr10(int&& num)       // Function with rvalue reference argument
{
   cout << endl << "Value received = " << num;
   num += 10;                  
   return num;              // Return the incremented value
}

4.2.3. Accepting a Variable Number of Function Arguments

int sumValues(int first,...)
{
    //Code for the function
}
  • Always be careful with the type that you want to enter!!!
// Handling a variable number of arguments
#include <iostream>
#include <cstdarg> 	// For handling a variable number of arguments
using namespace std;
        
int sum(int count, ...)
{
  if(count <= 0)
    return 0;
        
  va_list arg_ptr;                     // Declare argument list pointer
  va_start(arg_ptr, count);            // Set arg_ptr to 1st optional argument
        
  int sum(0);
  for(int i = 0; i<count; i++)
    sum += va_arg(arg_ptr, int);       // Add int value from arg_ptr and increment
        
  va_end(arg_ptr);                     // Reset the pointer to null
  
  return sum;
}
        
int main(int argc, char* argv[])
{
  cout << sum(6, 2, 4, 6, 8, 10, 12) << endl;     // The compiler will get the type of variable you entered, like [auto] does
  cout << sum(6, 2, 4, 6.1, 8, 10, 12) << endl;   // This will lead to error
  
  return 0;
}

4.2.4. Initializing Function Parameters

// Omitting function arguments
#include <iostream>

using namespace std;

void showit(const char message1[] = ">>>>>>", const char message2[] = "<<<<<<");

int main(void)
{
	const char mymess[] = "The end of the world is nigh.";
	
	showit();                                 // Display the basic message
	showit("Something is wrong!");            // Display an alternative
	showit();                                 // Display the default again
	showit(mymess);                           // Display a predefined message
	showit("Something is wrong!", mymess);   // Display an alternative
	cout << endl;
	return 0;
}

void showit(const char message1[], const char message2[])
{
	cout << endl
	     << "NO.1:" << message1;
	cout << endl
	     << "NO.2:" << message2;
	cout << endl;
	return;
}

4.2.5. Returning values from a function

4.2.5.1. Returning a Pointer

Never, ever, return the address of a local automatic variable from a function!!!

// Returning a Pointer
#include <iostream>
using std::cout;
using std::endl;

double* treble(double);                   // Function prototype

int main(void)
{
   double num(5.0);                       // Test value
   double* ptr(nullptr);                  // Pointer to returned value

   ptr = treble(num);

   cout << endl << "Three times num = " << 3.0*num;

   cout << endl << "Result = " << *ptr;   // Display 3*num
   delete ptr;                            // Don't forget to free the memory
   ptr = nullptr;				    
   cout << endl;
   return 0;
}

// Function to treble a value - mark 1
double* treble(double data)
{
   double *result=(new double(0.0));     // not use an automatic value to store a returning address
   *result = 3.0*data;
   return result;
}
4.2.5.2. Returning a Reference

Never, ever, return a reference to a local variable from a function!!!

// Returning a reference
#include <iostream>
#include <iomanip>

#define _countof(Array) sizeof(Array)/sizeof(*Array)
using namespace std;
        
double& lowest(double values[], const int& length); // Function prototype
        
int main(void)
{

   double data[] = { 3.0, 10.0, 1.5, 15.0, 2.7, 23.0,
                      4.5, 12.0, 6.8, 13.5, 2.1, 14.0 };
   int len(_countof(data));                 // Number of elements       
   for(auto value : data)
      cout << setw(6) << value;
        
   lowest(data, len) = 6.9;                 // Change lowest to 6.9
   lowest(data, len) = 7.9;                 // Change lowest to 7.9
        
   cout << endl;
   for(auto value : data)
      cout << setw(6) << value;
        
   cout << endl;
   return 0;
}
        
// Function returning a reference
double& lowest(double a[], const int& len)
{
   int j(0);                                 // Index of lowest element
   for(int i = 1; i < len; i++)
      if(a[j] > a[i])                        // Test for a lower value...
         j = i;                              // ...if so update j
   return a[j];                              // Return reference to lowest element
}
// Returning a pointer
#include <iostream>
#include <iomanip>

#define _countof(Array) sizeof(Array)/sizeof(*Array)
using namespace std;


double* lowest(double values[], const int& length); // Function prototype

int main(void)
{

   double data[] = { 3.0, 10.0, 1.5, 15.0, 2.7, 23.0,
                      4.5, 12.0, 6.8, 13.5, 2.1, 14.0 };
   int len(_countof(data));                 // Number of elements
   for(auto value : data)
      cout << setw(6) << value;

   *lowest(data, len) = 6.9;                 // Change lowest to 6.9
   *lowest(data, len) = 7.9;                 // Change lowest to 7.9
   *(0xffddef) = 0;
   cout << endl;
   for(auto value : data)
      cout << setw(6) << value;

   cout << endl;
   return 0;
}

// Function returning a reference
double* lowest(double a[], const int& len)
{
   int j(0);                                 // Index of lowest element
   for(int i = 1; i < len; i++)
      if(a[j] > a[i])                        // Test for a lower value...
         j = i;                              // ...if so update j
   return &a[j];                              // Return reference to lowest element
}
4.2.5.3. Trailing return type
  • Mainly used in function template
  • Alternative Function Syntax (C++ 11)
auto power(double x, int n)-> double // Function header
{                                    // Function body starts here...
	double result(1.0);              // Result stored here
	for(int i = 1; i <= n; i++)
		result *= x;
	return result;
}                                    // ...and ends here

4.2.6. Loops (Recursion & Iteration)

Recursion and iteration are two approach to solve repetitive tasks. In many ways, recursive algorithms are simple, elegant and extremely powerful. As the scale of the problem expands, bigger stack space needs to be allocated. So, try to turn basic recursion into iteration or tail recursion as much as possible!

4.2.6.1. Iteration
4.2.6.2. Recursion
  1. Basic Recursion
  2. Tail Recursion
    A recursive function is said to be tail recursive if all recursive calls within it are tail recursive. A recursive call is tail recursive when it is the last statement that will be executed within the body of a function and its return value is not a part of an expression. Tail-recursive functions are characterized as having nothing to do during the unwinding phase. When a compiler detects a call that is tail recursive, it overwrites the current activation record instead of pushing a new one onto the stack.
4.2.6.3. Example 1: F(n) = n!
  • Iteration
    n! = (n)(n – 1)(n – 2) . . . (1)
int fact_iteration(int n){
	for(int i=0, sum=1; i<n;){
		sum *= (++i);
	}
	return sum;
}
  • Basic Recursion
    F ( n ) { 1 if  n = 0 , n = 1 n F ( n − 1 ) if  n &gt; 1 F(n)\begin{cases} 1 &amp;\text{if } n=0,n=1 \\ nF(n−1) &amp;\text{if } n&gt;1 \end{cases} F(n){1nF(n1)if n=0,n=1if n>1
int fact(int n) {
	if (n < 0)
		return 0;
	else if (n == 0)
		return 1;
	else if (n == 1)
		return 1;
	else
		return n * fact(n - 1);    // n need to be preserved until the end of the recursion
}

F ( 4 ) = 4 ! F(4)=4! F(4)=4!
4!
在这里插入图片描述

  • Tail Recursion

F ( n , a ) { 1 if  n = 0 a if  n = 1 F ( n − 1 , n a ) if  n &gt; 1 F(n,a)\begin{cases} 1 &amp;\text{if } n=0 \\ a &amp;\text{if } n=1 \\ F(n-1, na) &amp;\text{if } n&gt;1 \end{cases} F(n,a)1aF(n1,na)if n=0if n=1if n>1

int fact_tail(int n, int a) {
	if (n < 0)
		return 0;
	else if (n == 0)
		return 1;
	else if (n == 1)
		return a;
	else
		return fact_tail(n - 1, n * a);   // n could be discarded before next call
}

F ( 4 ) = 4 ! F(4)=4! F(4)=4!
4!
4!

4.2.6.4. Example 2: Fibonacci Series

F ( n ) { 0 if  n = 0 1 if  n = 1 , n = 2 F ( n − 1 ) + F ( n − 2 ) if  n &gt; 2 F(n)\begin{cases} 0 &amp;\text{if } n=0 \\ 1 &amp;\text{if } n=1,n=2 \\ F(n-1)+F(n-2) &amp;\text{if } n&gt;2 \end{cases} F(n)01F(n1)+F(n2)if n=0if n=1,n=2if n>2

  • Iteration
long Fibonacci_tail_recursion(int n)
{
	long fibo=1, fibo1=1, fibo2=1;
	
	for(int i=2; i<n; i++){
		fibo = fibo1 + fibo2;
		fibo1 = fibo2;
		fibo2 = fibo;
	}
	
	return fibo;
}
  • Basic Recursion
long Fibonacci_recursion(int n)  
{
	if(n==0){
		return 0;
	}
	else if(n == 1 || n == 2){  
		return 1;  
	}  
	else{  
		return Fibonacci_recursion(n-1)+Fibonacci_recursion(n-2);
	}  
} 
  • Tail Recursion
long Fibonacci_tail_recursion(int n)
{
	return Fibonacci_tail_recursion_i(n, 1, 1);
}

long Fibonacci_tail_recursion_i(int n, long fibo1, long fibo2)
{
	if(n == 0){
		return 0;
	}
	else if(n == 1 || n == 2){
		return fibo2;
	}
	return Fibonacci_tail_recursion_i(n-1, fibo2, fibo1+fibo2);
}

4.2.7. Arguments to main()

//	argc	The count of the number of strings found on the command line
//	argv	An array that contains pointers to these strings plus an additional element that is null
int main(int argc, char* argv[])
{
	// Code for main()…
}
// Reading command line arguments
#include <iostream>
using std::cout;
using std::endl;
int main(int argc, char* argv[])
{
	cout << endl << "argc = " << argc << endl;
	cout << "Command line arguments received are:" << endl;
	for(int i = 0; i <argc; i++)
		cout << "argument " << (i+1) << ": " << argv[i] << endl;
	return 0;
}

4.2.8. function overloading

To enable the same operation to be performed with different operands using a single function name, we can define a series of functions with different parameter lists.
Function overloading provides you with the means of ensuring that a function name describes the function being performed and is not confused by extraneous information such as the type of data being processed. This is akin to what happens with basic operations in C++. To add two numbers, you use the same operator, regardless of the types of the operands.

!!! Attention:

  1. Return type can’t be used to distinguish a function!!! Because sometimes you call a function without using its return value.
/**	Overloaded Functions
 *	How to use
 */
// Using overloaded functions
#include <iostream>
using std::cout;
using std::endl;
#define _countof(_Array) (sizeof(_Array) / sizeof(*(_Array)))
int max(int data[], const int &len);       // Prototypes for
long max(long data[], const int &len);     // a set of overloaded
double max(double data[], const int &len); // functions

int main(void)
{
	int small[] = {1, 24, 34, 22};
	long medium[] = {23, 245, 123, 1, 234, 2345};
	double large[] = {23.0, 1.4, 2.456, 345.5, 12.0, 21.0};
	
	int lensmall(_countof(small));
	int lenmedium(_countof(medium));
	int lenlarge(_countof(large));
	
	cout << endl
	     << max(small, lensmall);
	cout << endl
	     << max(medium, lenmedium);
	cout << endl
	     << max(large, lenlarge);
	
	cout << endl;
	return 0;
}

// Maximum of ints
int max(int x[], const int &len)
{
	int maximum(x[0]);
	for (int i = 1; i < len; i++)
		if (maximum < x[i])
			maximum = x[i];
	return maximum;
}

// Maximum of longs
long max(long x[], const int &len)
{
	long maximum(x[0]);
	for (int i = 1; i < len; i++)
		if (maximum < x[i])
			maximum = x[i];
	return maximum;
}

// Maximum of doubles
double max(double x[], const int &len)
{
	double maximum(x[0]);
	for (int i = 1; i < len; i++)
		if (maximum < x[i])
			maximum = x[i];
	return maximum;
}
  1. Ensure the compiler can select an appropriate overload when using reference types for parameters in overloaded functions.
void f(int n);		//  When you call f() with an argument of type int, the compiler has no means of determining
void f(int& rn);	//  which function should be selected because either function is equally applicable 


void f(int& arg);	// Lvalue reference parameter
void f(int&& arg);	// Rvalue reference parameter

int num(5);
f(num);				// Calls f(int&)
f(2*num);			// Calls f(int&&)
f(25);				// Calls f(int&&)
f(num++);			// Calls f(int&&):	pass expression to parameter => num = num + 1
f(++num);			// Calls f(int&): 	num = num + 1 => pass num to parameter

/**
 *	prefix ++ & postfix ++
 */
#include <iostream>

using namespace std;
void prt(int &&num);
void prt(int &num);
int main()
{
  int num(0);
  prt(num++);
  
  num = 0;
  prt(++num);

  num = 0;
  prt(++num + 10);

  return 0;
}

void prt(int&& num)
{
  cout << "Rvalue:\t" << num << endl;
}

void prt(int &num)
{
  cout << "Lvalue:\t" << num << endl;
}

4.2.9. function template

To enable the same operation to be performed with different operands using a single function name, we can also use function template. It can make your code more concise. Using a template doesn’t reduce the size of your compiled program in any way.

/**	A template for the function max()
 *	You can also redefine the function with some specific types.
 *	You can see function: [float max(float x[], const int & len)]
 */
#include <iostream>

#define _countof(Array) sizeof(Array) / sizeof(*Array)

using namespace std;

template <class T>
T max(T x[], const int &len)
{
  T maximum(x[0]);

  for (int i = 1; i < len; i++)
    if (maximum < x[i])
      maximum = x[i];

  return maximum;
}

float max(float x[], const int & len)
{
  float maximum(x[0]);

  cout << endl
       << "This is not from template!" << endl;
  for (int i = 1; i < len; i++)
    if (maximum < x[i])
      maximum = x[i];

  return maximum;
}

int main()
{
  int small[] = {1, 24, 34, 22};
  long medium[] = {23, 245, 123, 1, 234, 2345};
  double large[] = {23.0, 1.4, 2.456, 345.5, 12.0, 21.0};
  float huge[] = {23.0, 1.4, 2.456, 345.5, 12.0, 21.0, 555.123};

  int lensmall(_countof(small));
  int lenmedium(_countof(medium));
  int lenlarge(_countof(large));
  int lenhuge(_countof(huge));

  cout << endl
       << max(small, lensmall);
  cout << endl
       << max(medium, lenmedium);
  cout << endl
       << max(large, lenlarge);
  cout << endl
       << max(huge, lenhuge);
  cout << endl;

  return 0;
}

4.3. exceptions

Exceptions are really intended to be applied in the context of exceptional, near-catastrophic conditions that might arise. So use if-else rather than exception as an alternative to the normal data checking and validating that you might do in a program.

Exception handles:

StatementEffect
tryIdentifies a code block in which an exception can occur.
throwCauses an exception condition to be originated and throws an exception of a particular type. If no catch block handle the error, it will lead to exception ends.
catchIdentifies a block of code that handles the exception. Catches errors be caused in corresponding try block.
  • try block: start with keyword try, end with one or more catch clauses.
  • exception class: be used to pass information about what happened between a throw and an associated catch.
try{
	if(/* The condition in which the error occurred */)
		throw value;
}
catch(type exception){
	// handler-statements
}

try{
	if(/* The condition in which the error occurred */)
		throw value;
}
catch(...){	// handle any exception thrown in a try block
	// do something
}
/**
 *	try-throw-catch
 */
#include <iostream>
using std::cout;
using std::endl;

int main(void)
{
  int counts[] = {34, 54, 0, 27, 0, 10, 0};
  int time(60);                        // One hour in minutes
  int hour(0);                         // Current hour
        
  for(auto& count : counts)
  {
    try
    {
      cout << endl << "Hour " << ++hour;
        
      if(0 == count)
		// data type should match the parameter in catch block
        throw "Zero count - calculation not possible.";
        
      cout << " minutes per item: " << static_cast<double>(time)/count;
    }
    catch(const char aMessage[])
    {
      cout << endl << aMessage << endl;
    }
  }
  return 0;
}
/**	Nested try blocks
 *	catch all the exceptions thrown in corresponding try block
 */
#include <iostream>
using namespace std;
       
int main(void)
{
  int height(0);
  const int minHeight(9);                        // Minimum height in inches
  const int maxHeight(100);                      // Maximum height in inches
  const double inchesToMeters(0.0254);
  char ch('y');
        
  try                                            // Outer try block
  {
    while('y' == ch || 'Y' == ch)
    {
      cout << "Enter a height in inches: ";
      cin >> height;                             // Read the height to be
                                                 // converted
        
      try                                        // Defines try block in which
      {                                          // exceptions may be thrown
        if(height > maxHeight)
           throw "Height exceeds maximum";       // Exception thrown
        if(height < minHeight)
           throw height;                         // Exception thrown
        
         cout << static_cast<double>(height)*inchesToMeters
              << " meters" << endl;
      }
      catch(const char aMessage[])               // start of catch block which
      {                                          // catches exceptions of type
        cout << aMessage << endl;                // const char[]
      }
      cout << "Do you want to continue(y or n)?";
      cin >> ch;
    }
  }
  catch(int badHeight)
  {
    cout << badHeight << " inches is below minimum" << endl;
  }
  
  return 0;
}

4.3.1. Rethrowing Exceptions

After some corrective actions, a catch may decide that the exception must be handled by a function further up the call chain. A catch passes its exception out to another catch by rethrowing the exception.

try
{}
catch(type1 exception)
{}
catch(type2 exception)
{
	//Process the exception handle… 
	throw;    //Rethrow the exception for processing by the caller}
/**
 *	Exception Rethrowing 
 */
#include <iostream>

using namespace std;

void convert(int height);

const int minHeight(9);                        // Minimum height in inches
const int maxHeight(100);                      // Maximum height in inches
const double inchesToMeters(0.0254);

int main(void)
{
  int height(0);
  char ch('y');
        
  try                                            // Outer try block
  {
    while('y' == ch || 'Y' == ch)
    {
      cout << "From Main   : Enter a height in inches: ";
      cin >> height;                              // Read the height to be
      convert(height);                           // converted
      cout << "From Main   : Do you want to continue(y or n)? ";
      cin >> ch;
    }
  }
  catch(int height)
  {
    cout << "From Main   : " << height << " meters exceeds maximum!\n" << endl;
  }
  return 0;
}

void convert(int height)
{  
    try                                         // Defines try block in which
    {                                           // exceptions may be thrown
      if(height > maxHeight)
          throw height;                         // Exception thrown
      if(height < minHeight)
          throw "From Convert: Height below minum!\n";      // Exception thrown
      
      cout << "From Convert: " << static_cast<double>(height)*inchesToMeters
          << " meters\n";
    }
    catch(int height)                           // start of catch block which
    {                                           // catches exceptions of type
      cout << "From Convert: " << height << " meters exceeds maximum!\n";
      cin >> height;
      throw;
    }
    catch(const char aMessage[])
    {
      cout << aMessage;
      throw;
    }
}


4.4. struct


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值