C++ topics by example

virtual destructor

Ch7 Functions

Ch8 Basic OO

constructor, copy constrcutor, assignment operator, shallow vs deep copying (constructor1.cpp, constructor2.cpp, shallow_deep_copy.cpp, )

Ch10 Composition

constrcutor init list
composition (main.cpp, Creature.h, Point2D.h)
    aggregation
container class (main.cpp, IntArray.h, IntArray.cpp)
const member functions
I/O overloading (friend functions)

Ch11 Inheritance

Ch12 Virtual Functions

Ch13 I/O

Ch15 Exceptions

throw, try, and catch
stack unwinding
int DoSomething() throw(); // does not throw exceptions
int DoSomething() throw(double); // may throw a double
int DoSomething() throw(...); // may throw anything
Note that exception handlers should catch class exception objects by reference instead of by value. This prevents the compiler from make a copy of the exception, which can be expensive when the exception is a class object. Catching exceptions by pointer should generally be avoided unless you have a specific reason to do so.
Rule: Handlers for derived exception classes should be listed before those for base classes.
Let.s take a look at this using std::exception. There are many classes derived from std::exception, such as std::bad_alloc, std::bad_cast, std::runtime_error, and others. When the standard library has an error, it can throw a derived exception correlating to the appropriate specific problem it has encountered.
std::auto_ptr is a template class that holds a pointer, and deallocates it when it goes out of scope.
Note that std::auto_ptr should not be set to point to arrays. This is because it uses the delete operator to clean up, not the delete[] operator.
In fact, there is no array version of std::auto_ptr! It turns out, there isn.t really a need for one. In the standard library, if you want to do dynamically allocated arrays, you.re supposed to use the std::vector class, which will deallocate itself when it goes out of scope.
Unlike constructors, where throwing exceptions can be a useful way to indicate that object creation succeeded, exceptions should not be thrown in destructors.

Ch16 STL

Sequence containers are container classes that maintain the insert ordering of elements.
Associative containers are containers that automatically sort their inputs when those inputs are inserted into the container. By default, associative containers compare elements using operator<.
Container adapters are special predefined containers that are adapted to specific uses. The interesting part about container adapters is that you can choose which sequence container you want them to use.
tips
STL algorithms
c++11 features

Apendix A

mutable: to handle the special cases where an object is logically constant, but for implementation reasons need to change. Eg, cash the expensive result
smart pointer: As you can see, auto_ptr is a simple wrapper around a regular pointer. It forwards all meaningful operations to this pointer (dereferencing and indirection). its smartness in the destructor: the destructor takes care of deleting the pointer. If we use a smart pointer, however, p will be cleaned up whenever it gets out of scope, whether it was during the normal path of execution or during the stack unwinding caused by throwing an exception.
check if the pointer is null (if(ptr)) before dereference
cast: static_cast, const_cast, reinterpret_cast, dynamic_cast

Appendix B

The code area, where the compiled program sits in memory.
The globals area, where global variables are stored.
The heap, where dynamically allocated variables are allocated from.
The stack, where parameters and local variables are allocated from.

The heap has advantages and disadvantages:
1) Allocated memory stays allocated until it is specifically deallocated (beware memory leaks).
2) Dynamically allocated memory must be accessed through a pointer.
3) Because the heap is a big pool of memory, large arrays, structures, or classes should be allocated here.

The call stack is a fixed-size chunk of sequential memory addresses.
The stack pointer keeps track of where the top of the stack currently is.
So what do we push onto our call stack? Parameters, local variables, and. function calls.
Because parameters and local variables essentially belong to a function, we really only need to consider what happens on the stack when we call a function. Here is the sequence of steps that takes place when a function is called:
The address of the instruction beyond the function call is pushed onto the stack. This is how the CPU remembers where to go after the function returns.
Room is made on the stack for the function.s return type. This is just a placeholder for now.
The CPU jumps to the function.s code.
The current top of the stack is held in a special pointer called the stack frame. Everything added to the stack after this point is considered .local. to the function.
All function arguments are placed on the stack.
The instructions inside of the function begin executing.
Local variables are pushed onto the stack as they are defined.

When the function terminates, the following steps happen:
The function's return value is copied into the placeholder that was put on the stack for this purpose.
Everything after the stack frame pointer is popped off. This destroys all local variables and arguments.
The return value is popped off the stack and is assigned as the value of the function. If the value of the function isn't assigned to anything, no assignment takes place, and the value is lost.
The address of the next instruction to execute is popped off the stack, and the CPU resumes execution at that instruction.

Typically, it is not important to know all the details about how the call stack works. However, understanding that functions are effectively pushed on the stack when they are called and popped off when they return gives you the fundamentals needed to understand recursion, as well as some other concepts that are useful when debugging.

The stack has advantages and disadvantages:
Memory allocated on the stack stays in scope as long as it is on the stack. It is destroyed when it is popped off the stack.
All memory allocated on the stack is known at compile time. Consequently, this memory can be accessed directly through a variable.
Because the stack is relatively small, it is generally not a good idea to do anything that eats up lots of stack space. This includes allocating large arrays, structures, and classes, as well as heavy recursion.

new/delete:
'malloc()' allocates a bunch of bytes while 'new' does the allocation as well as has the responsibility of calling the constructor.
'malloc' returns 'NULL' on failure while 'new' throws an exception object of type 'std::bad_alloc'. (There exists a nothrow version of 'new', though)
'malloc' is the C way of dealing with the free store while 'new' is the C++ way.
Every call to 'malloc()' should be followed by a call to 'free()' while every call to 'new' should be followed by a call to 'delete'.
'free()' deallocates all bytes allocated by a prior 'malloc()' call (using the same pointer) while 'delete' makes a call to the destructor and then goes on with the deallocation.
There are array forms of 'new' and 'delete' while 'malloc()' and 'free()' do not (as the request is for a specific size of raw memory in bytes).
'free()' on 'NULL' pointers is not safe while 'delete' is.

data allignment and data padding