Статические функции-члены классов в конечном итоге ничем не отличаются от обычных функций. Они действительно просто синтаксический сахар; функция просто имеет имя, которое включает Classname::
.
Нестатические элементы - это совсем другое дело. О нестатических функциях-членах (NSMF) следует помнить две важные вещи.
Во-первых, каждая нестатическая функция-член имеет доступ к нестатическим членам класса, членом которого они являются. Это возможно, даже если у вас может быть два объекта одного класса, которые хранят разные данные. Если у вас есть два std::string
объекта, каждый из них хранит разные строки. Выполнение find
для одной строки может вернуть найденный результат в одной, но не в другой.
Это потому, что каждый NSMF имеет неявный указатель this
. this
относится не только к классу, но и к фактическому объекту , над которым работает этот NSMF. Когда вы делаете это:
std::string aString("data");
aString.find("da");
Функция find
принимает строковый аргумент, но также получает aString
в качестве this
. Каждый раз, когда find
ищет членов своего класса, он будет смотреть на данные aString
.
Итак, давайте посмотрим на ваш предполагаемый вызов NSMF:
((*)nMemberFunction())
Где находится объект, от которого он получает указатель this
? Без объекта NSMF не мог бы получить доступ к нестатическим членам объекта, поскольку у него нет объекта, в котором они могли бы их найти. Это недопустимо.
Итак, правило № 1 о NSMF: вы должны вызывать их с фактическим экземпляром класса, членом которого является NSMF (или его производным классом). Вы не можете просто взять указатель NSMF и вызвать его как указатель на функцию; Вы должны вызывать его для живого объекта этого типа.
Правило № 2: синтаксис для указателей NSMF действительно ужасен.
Чтобы определить переменную (или аргумент) с именем arg
типа указателя NSMF, вы делаете это:
ReturnType (ClassName::*arg)(ParameterList);
Где ReturnType
- тип возвращаемого значения функции, ParameterList
- список аргументов, принятых функцией, а ClassName
- имя класса, которому принадлежит указатель NSMF.
Учитывая уродство, обычно лучше обернуть его в typedef:
typedef ReturnType (ClassName::*MemberPointerType)(ParameterList);
Таким образом создается typedef MemberPointerType
, который является указателем NSMF.
Учитывая объект с именем object
, который имеет тип ClassName
, вы бы назвали указатель члена arg
следующим образом:
ReturnType value = (object.*arg)(Params);
Где Params
- аргументы, которые вы хотите передать. Если object
является указателем на ClassName
вместо ссылки или значения, тогда вы используете object->*arg
вместо.
Еще одна вещь: вы должны использовать &
, чтобы получить имя указателя члена. В отличие от указателей на функции, указатели NSMF не преобразуются автоматически в указатели на элементы. Вы должны попросить их напрямую. Поэтому, если ClassName
имеет элемент с именем Function, который соответствует приведенным выше ReturnType
и ParameterList
, вы должны заполнить arg
следующим образом:
arg = &ClassName::Function;
Правило № 3: нестатические указатели на элементы не указатели . Да, они могут быть установлены в NULL (технически они могут быть установлены в 0), но они не то же самое, что указатель.
Большинство реальных компиляторов C и C ++ позволяют вам приводить указатель функции к void*
и обратно. Стандарты учитывают это неопределенное поведение, но это не совсем неизвестно. Вы абсолютно не можете сделать это с помощью указателя NSMF практически на всех компиляторах C ++. Действительно, sizeof(MemberPointerType)
, скорее всего, не будет такого же размера, как void*
.
Итак, указатели NSMF не являются обычными указателями. Не относитесь к ним как к таковым.