本文是在学习C++过程中所做的知识总结,是在阅读"Ivor_Horton’s_Beginning_Visual_Cpp_2012"和"Cpp_Primer_Plus"之余,记录的重点内容和个人知识盲区,以及与C语言相似特性的比较。方便忘记时,可以快速查阅。引用内容出处在此就不做一一标注。
文章目录
- 1. Variable
- 2. Operator
- 3. Statement
- 4. Structure
- 4.1. namespace
- 4.2. function
- 4.2.1. The General Form of a Function
- 4.2.2. Pass-Value Mechanism
- 4.2.3. Accepting a Variable Number of Function Arguments
- 4.2.4. Initializing Function Parameters
- 4.2.5. Returning values from a function
- 4.2.6. Loops (Recursion & Iteration)
- 4.2.7. Arguments to main()
- 4.2.8. function overloading
- 4.2.9. function template
- 4.3. exceptions
- 4.4. struct
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:
- the memory address of the function
- the parameter list for the function
- 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
- An alias for a variable
- Refers to a persistent storage location that can appear on the left of an assignment operation
1.2.2.2. rvalue references
- An alias for a variable
- Reference an rvalue, which is a temporary value that is essentially transient
- Important in the context of functions
Difference between pointers & reference
Type | Difference |
---|---|
pointer | 1. Find the address of pointer >>> 2. get the variable’s address from pointer >>> 3. get the variable by the address |
reference | Get 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];
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
Scope | Duration |
---|---|
global scope / global namespace scope / file scope | static variables |
local scope / block scope | static variables /automatic variables |
Four types of storage duration
Scope | Duration |
---|---|
Automatic storage duration | 1. 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 duration | a variable that’s defined and accessible locally, but continues to exist after exiting the block in which it is declared |
Dynamic storage duration | allocate space dynamically at execution time, existing from the time you allocate it, until you free it |
Thread Storage Duration | The 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
specifier | object vs function | info |
---|---|---|
auto | object | 1. automatic storage duration 2. permissible only in object declarations within a function 3. objects declared within a function have automatic storage duration by default |
register | object | 1. 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 |
static | object | 1. static storage duration 2. no linkage or internal linkage 3. internal linkage |
static | function | 1. internal linkage |
extern | object & function | 1. static storage duration 2. external linkage, that means you can use them anywhere in the entire program |
_Thread_local | object | 1. 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 Description | Duration | Scope | Linkage | How Declared |
---|---|---|---|---|
Automatic | Automatic | Block | None | In a block |
Register | Automatic | Block | None | In a block with the keyword register |
Static with no linkage | Static | Block | None | In a block with the keyword static |
Static with external linkage | Static | File | External | Outside all functions |
Static with internal linkage | Static | File | Internal | Outside 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.
- a variable whose value persists from one call of a function to the
/** [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;
}
- Static Variables in global scope
see “Storage duration & Variable scope”
2. Operator
2.1. Precedence
PRECEDENCE | OPERATORS | ASSOCIATIVITY |
---|---|---|
1 | :: | None |
2 | () [] -> . | Left |
2 | postfix ++ postfix – | Left |
2 | typeid | Left |
2 | const_cast dynamic_cast static_cast reinterpret_cast | Left |
3 | logical not ! one’s complement ~ | Right |
3 | unary + unary - | Right |
3 | prefix ++ prefix – | Right |
3 | address-of & indirection * | Right |
3 | type cast (type) | Right |
3 | sizeof decltype | Right |
3 | new 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 |
16 | Throw | Right |
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.
Type | Conversion Time | How Declared |
---|---|---|
static_cast | the conversion is checked statically (when program is compiled) | static_cast<the_type_to_convert_to>(expression) |
dynamic_cast | the conversion is checked dynamically (when program is executing) | dynamic_cast<the_type_to_convert_to>(expression) |
const_cast | removing the const-ness of an expression | const_cast<the_type_to_convert_to>(expression) |
reinterpret_cast | unconditional cast | reinterpret_cast<the_type_to_convert_to>(expression) |
old-style conversion | cover 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.
/** 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] 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);
- 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
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
- Basic Recursion
- 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 > 1 F(n)\begin{cases} 1 &\text{if } n=0,n=1 \\ nF(n−1) &\text{if } n>1 \end{cases} F(n){1nF(n−1)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!
- Tail Recursion
F ( n , a ) { 1 if n = 0 a if n = 1 F ( n − 1 , n a ) if n > 1 F(n,a)\begin{cases} 1 &\text{if } n=0 \\ a &\text{if } n=1 \\ F(n-1, na) &\text{if } n>1 \end{cases} F(n,a)⎩⎪⎨⎪⎧1aF(n−1,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.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 > 2 F(n)\begin{cases} 0 &\text{if } n=0 \\ 1 &\text{if } n=1,n=2 \\ F(n-1)+F(n-2) &\text{if } n>2 \end{cases} F(n)⎩⎪⎨⎪⎧01F(n−1)+F(n−2)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:
- 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;
}
- 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:
Statement | Effect |
---|---|
try | Identifies a code block in which an exception can occur. |
throw | Causes 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. |
catch | Identifies 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;
}
}