Pointer and Memory 
 Stack and Heap 
One way to visualize the stack is as a deck of cards. The current top card represents the current scope of the program, usually the function that is currently being executed. All variables declared inside the current function will take up memory in the top stack frame. If the current function func1() calls another function func2(), a new card is put on the deck so that func2() has its own stack frame to work with. Parameters of the function are copied into its stack frame. Stack frames are nice because they provide an isolated memory workspace for each function: variables declared in one function's stack frame won't be changed by another function, unless you explicitly let it to. When a function ends, all variables deciared in its stack frame goes away automatically.
The heap is an area of memory that is completely independent of the current function or stack frame. When a function creates a variable and put it on heap, it can continue to exist after the function ends. The heap is less structured than the stack: any function can add things to it, modify existing things in it, and delete things from it. And you need to remember to deallocate (delete) any memory that is no longer needed, unless it's allocated in some special way like using smart pointers.
 Pointer 
Point can be initialized with 'nullptr'. 'nullptr' is converted to false in a boolean expression.
int* pn = 0;    // very old way
pn = NULL;      // old way
pn = nullptr;   // new way
if (!pn)        // here it's true
    cout << "pn points to an invalid place." << endl;
*pn = 0;        // undefined behavior to dereference a null pointer
// pointer to dynamically allocated memory
pn = new int(123);
*pn = 456;
delete pn;
pn = nullptr;
// pointer to variable on stack
int n = 0;
pn = &n;
// pointer to instance of struct or class
Person* pPerson = getPerson("Tom");
bool isValidAge = pPerson && pPerson->age >= 0; // pPerson->age or (*pPerson).age
// pointer to dynamically allocated array
int size = 10;
int* prgnPrices = new int[size];
for(int i = 0; i < size; ++i)
    prgnPrices[i] = 10 + i;        // or *(prgnPrices + i)
delete[] prgnPrices;
prgnPrices = nullptr;
 Problem of NULL 
NULL is in fact 0 (defined as a macro). It can cause problems in overloaded functions, so 'nullptr' is introduced.
void print(const char* pszName) { cout << "name is " << (pszName? pszName : "unknown") << endl; }
void print(int nAge) { cout << "age is " << nAge << endl; }
print(NULL);     // calls print(int)
print(nullptr);  // calls print(const char*)
// additional methods
const char* pszName = 0;
print(pszName);                         // calls print(const char*)
print(static_cast<const char*>(NULL));  // calls print(const char*)
 Smart Pointers 
A smart pointer deallocates memory when it goes out of scope. This simplifies the program in situations like a function with many returns (so you don't need to delete the pointers before each of the returns).
The most important 2 smart pointers are std::unique_ptr and std::shared_ptr, defined in <memory>.
 unique_ptr 
unique_ptr is very similar to ordinary C-style pointer, and you don't need to delete it after using it.
auto pn = make_unique<int>();            // old way (before C++14): unique_ptr<int> pn(new int);
*pn = 0;
// or initialize it
auto pn1 = make_unique<int>(0);          // old way: unique_ptr<int> pn1(new int(0));
int size = 10;
auto prgn = make_unique<int[]>(size);    // old way: unique_ptr<int[]> prgn(new int[size]);
for (int i = 0; i < size; ++i)
    prgn[i] = i;
auto pPerson = make_unique<Person>(getPerson("Tom"));
bool isValidAge = pPerson && pPerson->age >= 0;
 shared_ptr 
shared_ptr allows for distributed ownership of the data. Each time a shared_ptr is assigned, a reference count is incremented indicating one more owner of the data; when a shared_ptr goes out of scope, the reference count is decremented. When the reference count goes to 0, the object referenced by the pointer is deallocated.
auto pn = make_shared<int>(123);
auto prgn = make_shared<int[]>(10);
prgn[0] = 0;
 Choose of smart pointers 
-  unique_ptr: use this by default in most situations
 -  shared_ptr: use this if you need shared ownership
 -  auto_ptr: it has a lot of problems, so it was deprecated in C++11/14 and removed in C++17.
 
 Pass By Reference 
By default, the parameter of a function is pass by value, ie. a copy of the variable is copied as argument passed to the function, and even if you change the variable in function, it won't affect the original variable. In order to change the value of the variable outside of the function, C uses pointer as parameter of the function. In C++, you can use reference as parameter to make the syntax much simpler.
int n = 0;
void inc(int n) {
    ++n;    // this won't change anything outside of this function
}
// C style
void inc(int* pn) {
    ++*pn;
}
inc(&n);
// C++ style
void inc(int& n) {
    ++n;
}
inc(n);
But inc(int) can take a literal as argument: 
inc(0); while inc(int&) cannot do this, unless using const reference or  rvalue reference.
ToDo (soon): add code example of rvalue ref.
Returning a big structure or class from a function is expensive due to the copy operation. The old way to avoid this problem is to pass a non-const reference as parameter to the function. Since C++11, directly returning structure or class from functions becomes efficient without copy operation any more due to move semantics.
ToDo (soon): add desc and code example of move semantics.
 Pass By Const Reference 
The purpose of passing by const reference is to avoid two things:
-  copying the object
 -  modifying the object in the function
 
void func(const string& str) {
    cout << str << endl;
}
string str = "abc";
func(str);
func("def");
 Type Inference 
Type inference allows compiler to auto deduce type of an expression. This is done with 2 keywords: auto & decltype.
 auto 
'auto' keyword has following uses:
// 1. deducing function's return type
auto getSqr(int n) {
    return n * n;
}
// 2. structured binding
auto [var1, var2, var3] = std::array {1, 2, 3};
// 3. deduce type of expression
auto n = 1;
auto prgn = make_unique<int[]>(10);
auto f = 1.1 / 2;
// 4. deduce type of non-type template parameters
// 5. decltype(auto)
// 6. alternative function syntax
// 7. generic lambda expression
ToDo (soon): add code examples to above block
Important: 'auto' strips away reference and const qualifier, so it may cause a copy unintentionally. The way to avoid this is to add 'const' and '&':
const string str = "abc";
const string& getString() {
    return str;
}
auto s1 = getString();         // this makes a copy of the string 'str'
const auto& s2 = getString();  // this won't cause a copy operation
 decltype 
'decltype' keyword takes an expression as argument and return its type. The difference between auto and decltype is that the latter doesn't strip reference and const qualifier.
auto f = 3.14;
decltype(f) s = f * 2 * 2;
const string str = "abc";
const string& getString() {
    return str;
}
decltype(getString()) s = getString();  // this is the same as 'const auto& s = getString();'
'decltype' doesn't seem to be very useful in examples above, but it's very powerful in context of templates.
ToDo (soon): add code example.