#9 Константные методы класса


... или «...обещаю, для тебя — никакой разницы. Ничего не изменится...»


Простое правило: если логическое состояние объекта после выполнения метода останется неизменным, то смело объявляйте его (метод) константным, а физически изменяющиеся переменные-члены класса объявляйте со спецификатором mutable.
Кстати, mutable можно применять только к нестатическим переменным-членам, что логично.
Для клиента класса не важно, изменили ли вы какую-нибудь переменную, которая для него ничего не значит. Для него важно, чтобы объект был тем же, чем был до этого, с логической точки зрения. И так, ещё раз: всегда предпочитайте физической константности логическую. И выбросьте из головы, что применение mutable — признак непрофессиональности. Это ложное наставление неопытных и глупых программистов. Все признанные мастера советуют и рекомендуют пользоваться mutable. Пример дальше.
...

#8 Указатели на функции, методы и переменные-члены


... или «очаровательные тройняшки с разным характером»


Симпатичные
Указатель на функцию
Объявляется следующим образом:
int (*pFunc)( double );
Дополнительные скобки нужны для того, чтобы дать компилятору понять, что не описывается прототип функции, именуемой pFunc, возвращающей int* и принимающей double. После объявления указателя на функцию, мы получаем переменную с именем pFunc, способную указывать на функции, сигнатура которых соответствует той, которая использовалась при объявлении указателя. Можем так же присвоить указателю нулевое значение. Однако, в отличие от указателя void*, способного ссылаться на любой тип данных, с функциями такого выполнить не удастся — нельзя создать универсальный указатель на функцию, способный ссылаться на любую функцию.
Можно так же объявить ссылку на функцию. Как и обычные ссылки, эта ссылка не может быть переназначена и должна быть инициализирована.
void someFunc( int _iTime );
void (&rFunc)( int ) = someFunc;
...

#7 Операторы приведения в стиле C++

... или «жертвуем красотой и скоростью во имя стабильности»


Кристофера Робина пришлось убрать, поскольку операторов
приведения всего 4. Почему мальчика? Его было проще всего.
Cat* pCat = (Cat*)pMammal;
Этот способ приведения в стиле C ужасен. Так говорят все признанные специалисты и опытные программисты. Он не осуществляет никаких проверок и малозаметен в коде, потому сложно обнаружить ошибку им генерируемую.


Кстати, в C++ можно использовать старый тип приведения с помощью другой формы записи:
typedef Cat* PCat;
Cat* pCat = PCat( pMammal );


Если мы используем преобразование в старом стиле, то компилятор вообще никогда не будет находить ошибки.


Новые же операторы работают медленнее (во многих случаях), плюс имеют настолько топорный вид в коде, что не заметить его невозможно. При чтении и поддержке кода — очевидный плюс. Они отвечают только за своё преобразование, и потому их приходится комбинировать (включать друг в друга), если мы хотим добиться сложного преобразования. Итак, операторы следующие:
const_cast<>
reinterpret_cast<>
static_cast<>
dynamic_cast<>


О их особенностях читайте в продолжении.
...

#6 Указатели на указатели и ссылки на указатели

... или «чем глубже в лес, тем... темнее... помидоры??»



Указатели на указатели называются  #многоуровневыми .

Если объявлен массив указателей, то указатель на первый его элемент, будет указателем на указатель:
int* _array[10];
int** pa = _array;

Иногда функции принимают указатели на указатели, чтобы изменить адрес, на который какой-то указатель ссылается. Такой пример работы очень плох. Но запомните его, чтобы уметь разобрать такое в чужом коде, а заодно и посмеяться над ним:
...

#5 Массивы как параметры функций

... или «— Хотел бы я сделать ещё запутаннее, да не смог»


Рассмотрим быстренько эту тему.
Когда массивы передаются в функцию как параметр, передаётся указатель на первый элемент массива. Это называется   разложением (decay)  .


Напишем функцию
void func( int _array[7] );
Из объявления следует, что она принимает указатель на тип int. Не более того. В эту функцию можно передать как простой указатель, так и массив другой размерности — пусть вас не смущает число 7. Следовательно, рассчитать количество элементов в массиве можно следующим образом (определяем функцию func):
void func( int _array[7] )
{
    int iSize = sizeof( _array ) / sizeof( _array[0] );
}


А что если это не массив вовсе? А мы просто получили указатель на целочисленный тип? Ведь сигнатуру функции можно переписать так:
void func( int* _array );
и её смысл останется абсолютно таким же.


...

#4 Про базовый класс и его наследников

... или «Блаженное неведение»

Всё просто: базовый класс НИЧЕГО не должен знать о своих наследниках. Это упростит создание новых или удаление имеющихся производных классов без лишних затрат труда и времени. Этот термин получил название
   #блаженное неведение   
...

#3 Две маленькие особенности константных ссылок

... или «...гигант в шкуре крохи»

Ссылки предполагают очень легкую и комфортную работу. Ну, да, нельзя выделить память и переназначить на другой объект. Но не всегда это и нужно.
Всегда и во всех книгах говорилось, что ссылка всего лишь маленький псевдоним на настоящий объект. И если объекта уже нет, а ссылка всё ещё существует, то всё будет очень печально.
Но!
...

#2 Константные указатели и указатели на константу

... или «Обрезаем права на работу с указателями по самые... указатели»

Не будем делать долгих и лирических вступлений. Мы же все тут профессионалы. Поговорим о спецификаторе const при объявлении указателей:
1: int* pi = new int( 5 );
2: const int* pci = new int( 5 );
3: int const* pci2 = new int( 5 );
4: int* const cpi = new int( 5 );
5: const intconst cpci = new int5 );
6: int constconst cpci = new int5 );
...

#1 Виртуальные функции и конструктор

... или «Да я функции вызывал, когда ты был ещё только в планах»

Виртуальные функции — штука хорошая. Однако приходится считаться с механизмами её реализации.
При вызове виртуальных функций в конструкторе базового класса произойдёт не совсем то, чего вы ожидаете.
Рассмотрим пример:
Классы Drvd1 и Drvd2 наследуются от класса Base. В классе Base определён метод
 virtual void init( int _i ) = 0; 

Допустим, он обязывает объекты производных классов инициализироваться по-своему, в зависимости от полученного целочисленного параметра. Для пущей убедительности сделаем его чисто виртуальным. Дело за малым: вызвать эти методы.
...