Для класса Date очень полезными будут операции d++ и ++d. Рассмотрим реализацию таких операторов.
++a --a) (общий случай)Префиксная унарная операция может быть определена двумя способами:
@a ~ a.operator@().@a ~ operator@(a).Определим эту операцию для класса Date
class Date
{
    …
    public:
    …
    // определение префиксного ++
    Date & operator++()
    {
        add_days(1);
        return *this;
    }
};
a++ a--)При перегрузке префиксной и постфиксной унарных операций встает вопрос, об интерпретации записи типа operator@.
В первых версиях языка префиксная и постфиксная операции определялись одинаково, но 1998 году для их разделения был введен фиктивный параметр типа int.
Постфиксная унарная операция также может быть определена двумя способами:
a@ ~ a.operator@(int).a@ ~ operator@(a, int).Рассмотрим ее реализацию для класса Date:
class Date
{
    …
    public:
    …
    // определение постфиксного ++
    // d++ возвращает значение до увеличения, 
    // значит возвращать нужно копию, а не ссылку.
    Date operator++(int)
    {
        Date d = *this;
        add_days(1); // ~ ++(*this);
        return d;
    }
};
Как видим, из-за копирования всего объекта операция d++ будет выполняется дольше, поэтому надо выбирать в пользу ++d. 
Это верно для своих типов, но для встроенных типов компилятор производит оптимизацию. В результате записи i++ и ++i, например, для переменных типа int, равноценны.
Операция != симметрична, поэтому предпочтительным выбором будет перегрузка оператора operator!= как внешней функции. Однако, для увеличения производительности, объявим функцию другом класса — таким образом она станет inline.
class Date
{
    …
    public:
    …
    friend bool operator!=(const Date &d1, const Date &d2)
    {
        return !(d1==d2);
    }  
};
Как видим, использование ранее определенной операции == позволяет не обращаться к внутренним полям класса напрямую. Так как описанная функция является inline, то в результате будет произведена следующая замена.
d1!=d2 ~ !(d1==d2) ~ !(d1.d == d2.d && d1.m == d2.m && d1.y == d2.y)
Попробуем реализовать аналог класса vector.
template<typename T>
class myvector
{
    T* data;
    int sz;
  public:
    myvector(int size): sz(size)
    {
        data = new T[sz];
    }
    int size()
    {
        return sz;
    }
};
Теперь напишем процедуру, которая выведет на экран содержимое нашего вектора.
void print(const myvector<int> &v)
{
    for(int i = 0 ; i < v.size(); i++)
    cout << v[i] << ' ';
}
Такой код не скомпилируется: ошибка произойдет из-за функции-члена v.size() так как эта функция, потенциально может изменить состояние объекта, в то время как формальный параметр v объявлен с модификатором const.
Для решения этой проблемы функцию size() надо определить как константный.
int size() const
{
    return sz;
}
Теперь рассмотрим перегрузку операции operator[] для того же класса myvector.
template <typename T>
class myvector
{
    T* data;
    int sz;
  public:
    …
    T& operator[](int n)
    {
        if(n < 0 || n >= sz)
            throw n;
        return data[n];
    }
};
Так как operator[] ― inline, то будет произведена замена:
v[i] ~ v.operator[](i) ~ v.data[i]
При компиляции процедуры print вновь произойдет ошибка. На этот раз причиной будет функция v[i]. Для корректной компиляции в данном случае необходимо определить вторую функцию.
T operator[](int n) const
{
    if(n < 0 || n >= sz)
        throw n;
    return data[n];
}
Деструктор — коренная особенность C++
Рассмотрим такой случай:
{
    myvector<int> v(100000);
    v[0] = v[99999];
    …
}
Внутри v создается массив в динамической памяти на 100000 элементов, а после работы с ним освобождения этого блока не происходит. Возникает утечка памяти. 
Для борьбы с утечками памяти в таких случаях в C++ был придуман деструктор.
Реализуем деструктор для класса myvector.
template <typename T>
class myvector
{
    T* data;
    int sz;
  public:
    ~myvector()
    {
        delete[] data;
    }
};
Деструкторы всегда вызываются неявно в определенный момент времени. Для локальных объектов при выходе из блока, а для глобальных при завершении программы.
Принципиальное отличие этого подхода от сборщика мусора: сборщик мусора вызывается в недетерминированный момент времени, а деструкторы всегда вызываются в детерминированный момент времени, когда заканчивается время жизни объекта.