Bogotobogo
contact@bogotobogo.com
Operator Overloading - 2012
- C++ Home
- String
- Constructor
- Operator Overloading
- Virtual Functions
- Dynamic Cast Operator
- Type Cast Operators
- Class auto_ptr
- References for Built-in Types
- Pass by Value vs. Pass by Reference
- Memory Allocation
- Friend Functions and Friend Classes
- Functors (Function Objects)
- Static Variables and Static Class Members
- Exceptions
- Stack Unwinding
- Pointers
- Pointers II - void pointers & arrays
- Pointers III - pointer to function & multi-dimensional arrays
- Taste of Assembly
- Small Programs
- Linked List Examples
- Binary Tree Example Code
- Templates
- Standard Template Library (STL) I
- Standard Template Library (STL) II - Maps
- Standard Template Library (STL) III - Iterators
- Standard Template Library (STL) IV - Algorithms
- Object Slicing and Virtual Table
- The this Pointer
- Stack Unwinding
- Upcasting and Downcasting
- Object Returning
- Private Inheritance
- Preprocessor - Macro
- C++_Keywords
- fstream: input & output
- Multi-Threaded Programming - Terminology
- Multi-Threaded Programming II - Native Thread for Win32 (A)
- Multi-Threaded Programming II - Native Thread for Win32 (B)
- Multi-Threaded Programming II - Native Thread for Win32 (C)
- Multi-Threaded Programming II - C++ Thread for Win32
- Multi-Threaded Programming III - C++ Class Thread for Pthreads
- Multithread Debugging
- Socket - Server & Client
- Embedded Systems Programming
- Boost
- make
- Debugging Crash & Memory Leak
- Libraries
- C++ API Testing
- Design Patterns in C++
- Algorithms in C++
- Programming Questions and Solutions
- Blackjack with Qt
A default argument is a value that will be used automatically if we omit the corresponding actual argument when we call a function. How do we set a default value? We must use the function prototype because the compiler looks at the prototype to check how many arguments a function uses.
For example:
int f(const char *s, int n = 2012)We want the function to return a new int, so its type is int. We want n to have a default value of 2012, so we assign that value to n. If we leave n alone, it has the value 2012, but if we pass an argument, the new value overwrites the 2012
When we use a function with an argument list, we must add defaults from right to left. In other words, we can't provide a default value for a particular argument unless we also provide defaults for all the arguments to its right:
float f1(int l, int m = 2, int n = 3); // OK float f2(int l, int m = 2, int n); // Not OK
Function overloading lets us use multiple functions sharing the same name. We usually utilize the function overloading to design a family of functions that do the same thing while using different argument lists.
The key to function overloading is a function's argument list (function signature). If two functions use the same number and types of arguments in the same order, they have the same signature. C++ allows us to define two multiple functions by the same name, provided that the functions have different signatures. The signature can differ:
- in the number of arguments
- or in the type of arguments
- or both
For example, we defines several versions of f() with the following prototypes:
void f(const char *s, int n); // (a) void f(double d, int n); // (b) void f(long l, int n); // (c) void f(int m, int n); // (d) void f(const char *s); // (e)
Some signatures which appears to be different but actually both have the same signature:
double square(double d); double square(double &d);
But we need to look with compiler's perspective. To call them, we use:
square(z);
and the z argument matches both double d and the double &d, thus the compiler has no way of knowing which function to use. So, to avoid such ambiguity, when it checks function signatures, the compiler considers a reference to a type and the type itself to be the same signature.
Note that in the function-matching process, the compiler discriminate between const and non-const variables:
int f(char *s); // overloaded int f(const char *s); // overloaded
Also note that the signature, not the return type, enables function overloading. For instance, the following two have the same signature, and can't not be overloaded.
float f(int m, int *n) // Not overloaded double f(int m, int *n) // Not overloaded
Operator overloading extends the overloading concept to operators so that we can assign new meanings to C++ operators. It lets us extend operator overloading to user-defined types. That is by allowing us to use the "+" to add two objects. The compiler determines which definition of addition to use depending on the number and type of operands. Overloaded operators can often make code look more natural. In other words, operator overloading can be very useful to make our class look and behave more like built-in types.
To overload an operator, we use a special function, operator function. For example, when we overload "+":
operator+(argument_list)
Suppose, for example, that we have a MyComplex class for which we define an operator+() member function to overload the + operator so that it adds one complex number to another complex number. Then, if c1, c2, c3 are all objects of MyComplex class, we can write this:
c3 = c1 + c2;
The compiler, recognizing the operands as belonging to the MyComplex class, replaces the operator with the corresponding operator function:
c3 = c1.operator+(c2);
The function then use the c1 object which invokes the method, and the c2 object is passed as an argument to calculate the sum, and returns it. Note that we use assignment operator = which is also need to be overload.
The following operators cannot be overloaded:
- . member selection
- .* member selection with pointer-to-member
- ?: conditional
- :: scope resolution
- # stringizing operator
- ## merging operator
- sizeof object size information
- typeid object type information
In this section we'll learn how to overload the assignment (=) operator between two objects of the Complex class.
Let's look at the following code:
class MyComplex
{
private:
double real, imag;
public:
MyComplex(){
real = 0;
imag = 0;
}
MyComplex(double r, double i) {
real = r;
imag = i;
}
double getReal() const {
return real;
}
double getImag() const {
return imag;
}
MyComplex & operator=(const MyComplex &);
};
MyComplex & MyComplex::operator=(const MyComplex& c) {
real = c.real;
imag = c.imag;
return *this;
}
#include <iostream>
int main()
{
using namespace std;
MyComplex c1(5,10);
MyComplex c2(50,100);
cout << "c1= " << c1.getReal() << "+" << c1.getImag() << "i" << endl;
cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;
c2 = c1;
cout << "assign c1 to c2:" << endl;
cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;
}
We get the output as expected:
c1= 5+10i c2= 50+100i assign c1 to c2: c2= 5+10i
In this section we'll learn how to overload the addition (+) operator between two objects of the Complex class.
Let's look at the following code which has '+' additional overloading function:
class MyComplex
{
private:
double real, imag;
public:
MyComplex(){
real = 0;
imag = 0;
}
MyComplex(double r, double i) {
real = r;
imag = i;
}
double getReal() const {
return real;
}
double getImag() const {
return imag;
}
MyComplex & operator=(const MyComplex &);
MyComplex & operator+(const MyComplex& );
};
MyComplex & MyComplex::operator=(const MyComplex& c) {
real = c.real;
imag = c.imag;
return *this;
}
MyComplex & MyComplex::operator+(const MyComplex& c) {
real += c.real;
imag += c.imag;
return *this;
}
#include <iostream>
int main()
{
using namespace std;
MyComplex c1(5,10);
MyComplex c2(50,100);
cout << "c1= " << c1.getReal() << "+" << c1.getImag() << "i" << endl;
cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;
c2 = c1;
cout << "assign c1 to c2:" << endl;
cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;
cout << endl;
MyComplex c3(10,100);
MyComplex c4(20,200);
cout << "c3= " << c3.getReal() << "+" << c3.getImag() << "i" << endl;
cout << "c4= " << c4.getReal() << "+" << c4.getImag() << "i" << endl;
MyComplex c5 = c3 + c4;
cout << "adding c3 and c4" << endl;
cout << "c3= " << c3.getReal() << "+" << c3.getImag() << "i" << endl;
cout << "c4= " << c4.getReal() << "+" << c4.getImag() << "i" << endl;
cout << "c5= " << c5.getReal() << "+" << c5.getImag() << "i" << endl;
}
Note that when we're using '+' for the object of MyComplex type,
c5 = c3 + c4;actually, we are calling a function something like this.
c5 = c3.operator+(c4)
Output of the code above is:
c1= 5+10i c2= 50+100i assign c1 to c2: c2= 5+10i c3= 10+100i c4= 20+200i adding c3 and c4 c3= 30+300i c4= 20+200i c5= 30+300i
We got the right result at least for c5. But the value of c3 has been changed.
What happened?
Let look at the code overloading '+'.
MyComplex & MyComplex::operator+(const MyComplex& c) {
real += c.real;
imag += c.imag;
return *this;
}
As it turned out, the operation inside the function returning the reference to c3 object which has been changed.
So, let's rewrite the overloading function.
const MyComplex operator+(const MyComplex & );
const MyComplex MyComplex::operator+(const MyComplex& c) {
MyComplex temp;
temp.real = this->real + c.real;
temp.imag = this->imag + c.imag;
return temp;
}
Note that this doesn't return Complex &, but instead returns a const Complex class variable. As you can see, the implementation is a little bit different from the previous example. Here, we're not returning *this. Instead, we're creating a temporary variable and assigning the results of the addition to temp. This explains why the function returns const Complex and not Complex &. In other words, the function creates a new MyComplex object temp that represents the sum of the other two MyComplex objects. Returning the object creates a copy of the object that the calling function can use. If the return type were MyComplex &, however, the reference would be the temp object. But the temp object is a local variable and is destroyed when the function terminates, so the reference would be a reference to a nonexisting object. Using a MyComplex return type, however, means the program constructs a copy of MyComplex object before destroying it, and the calling function gets the copy.
Why we're returning const? Look Object Returning.
Then, we'll get the right answer.
c1= 5+10i c2= 50+100i assign c1 to c2: c2= 5+10i c3= 10+100i c4= 20+200i adding c3 and c4 c3= 10+100i c4= 20+200i c5= 30+300i
We get the same result if we use a friend function which is global and not a member of our MyComplex class.
Since this function will need to access the private members of MyComplex, we'll need to declare it as a friend function.
friend const MyComplex operator+(const MyComplex&, const MyComplex&);
The prototype has two implications:
- Although the operator+() function is not a member function, it has the same access rights as a member function.
- Although the operator+() function is declared in the class declaration, it is not a member function. So it isn't invoked by using the membership operator.
Our revised code is:
class MyComplex
{
private:
double real, imag;
public:
MyComplex(){
real = 0;
imag = 0;
}
MyComplex(double r, double i) {
real = r;
imag = i;
}
double getReal() const {
return real;
}
double getImag() const {
return imag;
}
MyComplex & operator=(const MyComplex &);
friend const
MyComplex operator+(const MyComplex&, const MyComplex&);
};
MyComplex & MyComplex::operator=(const MyComplex& c) {
real = c.real;
imag = c.imag;
return *this;
}
/* This is not a member function of MyComplex class */
const MyComplex operator+(const MyComplex& c1, const MyComplex& c2) {
MyComplex temp;
temp.real = c1.real + c2.real;
temp.imag = c1.imag + c2.imag;
return temp;
}
#include <iostream>
int main()
{
using namespace std;
MyComplex c1(5,10);
MyComplex c2(50,100);
cout << "c1= " << c1.getReal() << "+" << c1.getImag() << "i" << endl;
cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;
c2 = c1;
cout << "assign c1 to c2:" << endl;
cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;
cout << endl;
MyComplex c3(10,100);
MyComplex c4(20,200);
cout << "c3= " << c3.getReal() << "+" << c3.getImag() << "i" << endl;
cout << "c4= " << c4.getReal() << "+" << c4.getImag() << "i" << endl;
MyComplex c5 = c3 + c4;
cout << "adding c3 and c4" << endl;
cout << "c3= " << c3.getReal() << "+" << c3.getImag() << "i" << endl;
cout << "c4= " << c4.getReal() << "+" << c4.getImag() << "i" << endl;
cout << "c5= " << c5.getReal() << "+" << c5.getImag() << "i" << endl;
}
Note that, in the above example, we used one of the two forms for overloading operator+().
What we used was nonmember version:
MyComplex operator+(const MyComplex& c1, const MyComplex& c2);
But there is another form which is member version:
MyComplex operator+(const MyComplex& c1);
For the member function version, one is passed implicitly via the this pointer and the second is passed explicitly as a function argument. For the friend version, which is nonmember version, both are passed as argument.
Either of these two prototypes matches the express c1+c2, where c1 and c2 are type MyComplex objects. That is, the compiler can convert the statement
c3 = c1 + c2;
to either of the following:
c3 = opetator+(c1,c2); // nonmember function c3 = c1.operator+(c2); // member function
Output streams use the << operator for standard types. We can also overload the << operator for our own classes.
Actually, the << is left shift bit manipulation operator. But the ostream class overloads the operator, converting it into an output tool. The cout is an ostream object and that it is smart enough to recognize all the basic C++ types. That's because the ostream class declaration includes an overloaded operator<<() definition for each of the basic types.
#include <iostream>
using namespace std;
class MyComplex
{
private:
double real, imag;
public:
MyComplex(){
real = 0;
imag = 0;
}
MyComplex(double r, double i) {
real = r;
imag = i;
}
double getReal() const {
return real;
}
double getImag() const {
return imag;
}
MyComplex & operator=(const MyComplex &);
const MyComplex operator+(const MyComplex & );
MyComplex & operator++(void);
MyComplex operator++(int);
/*friend const
MyComplex operator+(const MyComplex&, const MyComplex&); */
friend ostream& operator<<(ostream& os, const MyComplex& c);
};
MyComplex & MyComplex::operator=(const MyComplex& c) {
real = c.real;
imag = c.imag;
return *this;
}
const MyComplex MyComplex::operator+(const MyComplex& c) {
MyComplex temp;
temp.real = this->real + c.real;
temp.imag = this->imag + c.imag;
return temp;
}
//pre-increment
MyComplex & MyComplex::operator++() {
real++;
imag++;
return *this;
}
//post-increment
MyComplex MyComplex::operator++(int) {
MyComplex temp = *this;
real++;
imag++;
return temp;
}
/* This is not a member function of MyComplex class */
/*
const MyComplex operator+(const MyComplex& c1, const MyComplex& c2) {
MyComplex temp;
temp.real = c1.real + c2.real;
temp.imag = c1.imag + c2.imag;
return temp;
}*/
ostream& operator<<(ostream &os, const MyComplex& c) {
os << c.real << '+' << c.imag << 'i' << endl;
return os;
}
int main()
{
MyComplex c1(5,10);
cout << "c1 = " << c1.getReal() << "+" << c1.getImag() << "i" << endl;
cout << "Using overloaded << " << endl;
cout << "c1 = " << c1 << endl;
}
Output is:
c1 = 5+10i Using overloaded << c1 = 5+10i
Note that we just used:
cout << "c1 = " << c1 << endl;
Note that when we do
cout << c1;
it becomes the following function call:
operator<<(cout, c1);
In this section, we'll learn how to overload increment and decrement operators (++ and --). We'll put a focus on the increment operators since the decrement operators are working in the same way. There are two types of increment operators actually: pre-increment(++i) and post-increment(i++).
Note the following:
- v++
(post) increment; this is a postfix expression, and the value of v++ is the value of v before the increment. - ++v
(pre) increment; this is a unary expression, and the value of ++v is the value of v after the increment.
Here is the modified code with the overloaded increment functions.
class MyComplex
{
private:
double real, imag;
public:
MyComplex(){
real = 0;
imag = 0;
}
MyComplex(double r, double i) {
real = r;
imag = i;
}
double getReal() const {
return real;
}
double getImag() const {
return imag;
}
MyComplex & operator=(const MyComplex &);
const MyComplex operator+(const MyComplex & );
MyComplex & operator++(void);
MyComplex operator++(int);
/* friend const
MyComplex operator+(const MyComplex&, const MyComplex&); */
};
MyComplex & MyComplex::operator=(const MyComplex& c) {
real = c.real;
imag = c.imag;
return *this;
}
const MyComplex MyComplex::operator+(const MyComplex& c) {
MyComplex temp;
temp.real = this->real + c.real;
temp.imag = this->imag + c.imag;
return temp;
}
//pre-increment
MyComplex & MyComplex::operator++() {
real++;
imag++;
return *this;
}
//post-increment
MyComplex MyComplex::operator++(int) {
MyComplex temp = *this;
real++;
imag++;
return temp;
}
/* This is not a member function of MyComplex class */
/*
const MyComplex operator+(const MyComplex& c1, const MyComplex& c2) {
MyComplex temp;
temp.real = c1.real + c2.real;
temp.imag = c1.imag + c2.imag;
return temp;
}*/
#include <iostream>
int main()
{
using namespace std;
MyComplex c1(5,10);
MyComplex c2(50,100);
cout << "c1= " << c1.getReal() << "+" << c1.getImag() << "i" << endl;
cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;
c2 = c1;
cout << "assign c1 to c2:" << endl;
cout << "c2= " << c2.getReal() << "+" << c2.getImag() << "i" << endl;
cout << endl;
MyComplex c3(10,100);
MyComplex c4(20,200);
cout << "c3= " << c3.getReal() << "+" << c3.getImag() << "i" << endl;
cout << "c4= " << c4.getReal() << "+" << c4.getImag() << "i" << endl;
MyComplex c5 = c3 + c4;
cout << "adding c3 and c4" << endl;
cout << "c3= " << c3.getReal() << "+" << c3.getImag() << "i" << endl;
cout << "c4= " << c4.getReal() << "+" << c4.getImag() << "i" << endl;
cout << "c5= " << c5.getReal() << "+" << c5.getImag() << "i" << endl;
cout << endl;
++c5;
cout << "c5= " << c5.getReal() << "+" << c5.getImag() << "i" << endl;
c5++;
cout << "c5= " << c5.getReal() << "+" << c5.getImag() << "i" << endl;
}
The output is:
c1= 5+10i c2= 50+100i assign c1 to c2: c2= 5+10i c3= 10+100i c4= 20+200i adding c3 and c4 c3= 10+100i c4= 20+200i c5= 30+300i c5= 31+301i c5= 32+302i
There is one problem with defining both the prefix and postfix opetators: They each take the same number and type of parameters. Normal overloading cannot distinguish between whether the operator we're defining is the prefix version or the postfix.
To solve this problem, the postfix operator function take an extra parameter of the int. When we use the postfix operator, the compiler supplies 0 as the argument for this parameter. Although our postfix function could use this extra parameter, it usually should not. That parameter is not needed for the work normally performed by a postfix operator. Its sole purpose is to distinguish the definition of the postfix function from the prefix version.
Note that the post increment(v++) is returning an old value, and requires local temp variable. This is why we prefer prefix in STL iterators. Also, note that post increment(v++) is returning an object because it's a locally created, and cannot return a reference unlike the pre increment(++v) case which returns reference. Returning a Non-const Object.
The increment(++) and decrement(--) operators are most often implemented for class, such as iterators, that provide pointerlike behavior on the elements of a sequence. In the example below, we define a class that points to an array and provides access to the elements in the array.
#include <iostream>
class SmartPtr
{
public:
SmartPtr(int *b, int *e):beg(b), end(e), curr(b) {}
SmartPtr operator++(int);
SmartPtr& operator++();
int* getCurrent() {
return curr;
}
private:
int *beg;
int *end;
int *curr;
};
SmartPtr& SmartPtr::operator++()
{
if(curr == end)
throw "increment past the end";
++curr;
return *this;
}
SmartPtr SmartPtr::operator++(int)
{
SmartPtr ret(*this);
++*this;
return ret;
}
int main( )
{
using namespace std;
int a[] = {1,2,3,4,5};
try {
SmartPtr *ptr = new SmartPtr(a,a+5);
cout << *(ptr->getCurrent()) << endl;
(*ptr)++;
cout << *(ptr->getCurrent()) << endl;
}
catch (const char* e) {
cout << "exception: " << e << endl;
}
return 0;
}
Let's look at the postfix version. It is a bit more involved than the prefix operators. They should remember the current state of the object before incrementing the object. This operator defines a local SmartPtr, which is initialized as a copy of *this. In other words, the variable ret is a copy of the current state of this object.
Having kept a copy of the current state, the operator calls its own prefix operator to do the increment:++*this;
calls the SmartPtr prefix increment operator on this object. That operator checks that the increment is safe and either increments curr or throws an exception. If no exception was thrown, the postfix function completes by returning the stored copy in ret. Therefore, after the return, the object itself has been advanced, buit the value returned reflects the original, unincremented value.
SmartPtr& SmartPtr::operator++()
{
if(curr == end)
throw "increment past the end";
++curr;
return *this;
}
To access an element of an array/vector, we need to make [] working. We're going to start from the simplest example as below:
#include <iostream>
#include <string>
using namespace std;
class Vector
{
int sz;
double *elem;
public:
Vector(int s): sz(s), elem(new double[s]) {
for (int i = 0; i < s; i++) elem[i] = 0;
}
~Vector() { delete[] elem; }
int size() const { return sz; }
void set(int n, double val) {
elem[n] = val;
}
double get(int n) {
return elem[n];
}
};
int main()
{
Vector v(10);
for (int i = 0; i < v.size() ; i++) {
v.set(i, i*1.1);
}
for (int i = 0; i < v.size() ; i++) {
cout << v.get(i) << " " << endl;
}
return 0;
}
Here, we use set() and get(), which are kind of working but ugly. Also, we can't not use index operator, []. So, let's modify it a little bit:
#include <iostream>
#include <string>
using namespace std;
class Vector
{
int sz;
double *elem;
public:
Vector(int s): sz(s), elem(new double[s]) {
for (int i = 0; i < s; i++) elem[i] = 0;
}
~Vector() { delete[] elem; }
int size() const { return sz; }
double operator[](int n) { return elem[n]; }
};
int main()
{
Vector v(10);
double dval = v[3]; // OK
v[4] = 100; // error: '=' : left operand must be l-value
return 0;
}
In the above example, v[i] is interpreted as v.operator[](i). It returns the value of i-th element of v. However, the v[4] is just a value, not a variable as indicated by the error.
If we modify the overloading [] part, it looks a little bit better:
double *operator[](int n) { return &elem[n]; } // return a pointer
....
int main()
{
Vector v(10);
double dval = *v[3]; // OK
*v[4] = 100; // OK
cout << "*v[4] = " << *v[4] << endl; //OK
return 0;
}
But still we need dereference (*) the v to get/set. So, here comes the final version that we can use []:
#include <iostream>
#include <string>
using namespace std;
class Vector
{
int sz;
double *elem;
public:
Vector(int s): sz(s), elem(new double[s]) {
for (int i = 0; i < s; i++) elem[i] = 0;
}
~Vector() { delete[] elem; }
int size() const { return sz; }
double &operator[](int n) { return elem[n]; } // return reference
};
int main()
{
Vector v(10);
double dval = v[3]; // OK
v[4] = 100; // OK
cout << "v[4] = " << v[4] << endl; //OK
return 0;
}
Finally, v[i] is interpreted as v.operator[](i), and it returns a reference of the i-th element of v.
We can define operators either as members of our class or as non-member functions. Some member must be defined as class members, but others can be defined either way. As an example, let's look at the following code implementing *= operator:
class Complex
{
public:
Complex(double r, double i);
Complex *= (const Complex &c);
...
};
But we can use non-member operator:
class Complex
{
public:
Complex(double r, double i);
...
};
Complex &operator*=(Complex &lhs, const Complex &rhs);
The following operators should be declared as member methods to ensure that they receive an lvalue as their first operand:
- = assignment
- [] subscript
- -> class member access
- ->* pointer to member selection
- new/delete
- () function call
- (T) conversion (C-style cast)
Operators other than listed above, can be overloaded either as members or as non-members. But in general non-member overloading is recommended. The reasons are as below:
- Symmetry
When a binary operator is defined as a method of a class, it must have an object as the lhs operand. For example, * operator, we should be able to write like complex*10 but not like 10*complex because 10.operator*(complex) does not make any sense. In other words, a*b should be the same as b*a. Otherwise it breaks the commutitiveness that users are expecting from * operator. So, in this case, we should use non-member operator overloading. - Weak coupling
Since a non-member method cannot access private member, it tend to make the class less coupled.
More to come...
- C++ Home
- String
- Constructor
- Operator Overloading
- Virtual Functions
- Dynamic Cast Operator
- Type Cast Operators
- Class auto_ptr
- References for Built-in Types
- Pass by Value vs. Pass by Reference
- Memory Allocation
- Friend Functions and Friend Classes
- Functors (Function Objects)
- Static Variables and Static Class Members
- Exceptions
- Stack Unwinding
- Pointers
- Pointers II - void pointers & arrays
- Pointers III - pointer to function & multi-dimensional arrays
- Taste of Assembly
- Small Programs
- Linked List Examples
- Binary Tree Example Code
- Templates
- Standard Template Library (STL) I
- Standard Template Library (STL) II - Maps
- Standard Template Library (STL) III - Iterators
- Standard Template Library (STL) IV - Algorithms
- Object Slicing and Virtual Table
- The this Pointer
- Stack Unwinding
- Upcasting and Downcasting
- Object Returning
- Private Inheritance
- Preprocessor - Macro
- C++_Keywords
- fstream: input & output
- Multi-Threaded Programming - Terminology
- Multi-Threaded Programming II - Native Thread for Win32 (A)
- Multi-Threaded Programming II - Native Thread for Win32 (B)
- Multi-Threaded Programming II - Native Thread for Win32 (C)
- Multi-Threaded Programming II - C++ Thread for Win32
- Multi-Threaded Programming III - C++ Class Thread for Pthreads
- Multithread Debugging
- Socket - Server & Client
- Embedded Systems Programming
- Boost
- make
- Debugging Crash & Memory Leak
- Libraries
- C++ API Testing
- Design Patterns in C++
- Algorithms in C++
- Programming Questions and Solutions
- Blackjack with Qt