Я наткнулся на этот странный фрагмент кода, который прекрасно компилируется:
class Car
{
public:
int speed;
};
int main()
{
int Car::*pSpeed = &Car::speed;
return 0;
}
Почему C ++ имеет этот указатель на нестатический член данных класса? Какая польза от этого странного указателя в реальном коде?
Ответы:
Это «указатель на член» - следующий код иллюстрирует его использование:
Что касается того, почему вы хотели бы сделать это, это дает вам еще один уровень косвенности, который может решить некоторые сложные проблемы. Но, честно говоря, мне никогда не приходилось использовать их в своем собственном коде.
Редактировать: я не могу придумать убедительное использование указателей на данные членов. Указатель на функции-члены можно использовать в подключаемых архитектурах, но повторный пример в небольшом пространстве меня побеждает. Следующее - моя лучшая (не проверенная) попытка - функция Apply, которая выполняла бы некоторую предварительную и последующую обработку перед применением выбранной пользователем функции-члена к объекту:
Круглые скобки
c->*func
необходимы, потому что->*
оператор имеет более низкий приоритет, чем оператор вызова функции.источник
Это самый простой пример, который я могу себе представить, который передает редкие случаи, когда эта особенность уместна:
Здесь следует отметить указатель, передаваемый count_fruit. Это избавляет вас от необходимости писать отдельные функции count_apples и count_oranges.
источник
&bowls.apples
и&bowls.oranges
?&bowl::apples
и&bowl::oranges
не указывает ни на что.&bowl::apples
и&bowl::oranges
не указывать на членов объекта ; они указывают на членов класса . Они должны быть объединены с указателем на реальный объект, прежде чем они указывают на что-то. Эта комбинация достигается с->*
оператором.Другим приложением являются навязчивые списки. Тип элемента может сказать списку, каковы его следующие / предыдующие указатели. Таким образом, список не использует жестко закодированные имена, но все еще может использовать существующие указатели:
источник
next
.Вот реальный пример, над которым я сейчас работаю, из систем обработки сигналов / управления:
Предположим, у вас есть структура, которая представляет данные, которые вы собираете:
Теперь предположим, что вы помещаете их в вектор:
Теперь предположим, что вы хотите вычислить некоторую функцию (скажем, среднее) одной из переменных в диапазоне выборок, и вы хотите преобразовать это среднее значение в функцию. Указатель на член упрощает:
Примечание Отредактировано 2016/08/05 для более краткого подхода к шаблонам
И, конечно, вы можете создать шаблон для вычисления среднего значения для любого прямого итератора и любого типа значения, который поддерживает сложение с самим собой и деление на size_t:
РЕДАКТИРОВАТЬ - приведенный выше код влияет на производительность
Вы должны заметить, как я вскоре обнаружил, что приведенный выше код имеет некоторые серьезные последствия для производительности. Суть в том, что если вы вычисляете сводную статистику по временному ряду или вычисляете БПФ и т. Д., То вы должны хранить значения для каждой переменной непрерывно в памяти. В противном случае итерация по серии вызовет пропадание кэша для каждого полученного значения.
Рассмотрим производительность этого кода:
На многих архитектурах один экземпляр
Sample
заполнит строку кэша. Таким образом, на каждой итерации цикла один образец будет извлекаться из памяти в кэш. Будут использованы 4 байта из строки кэша, а остальные будут выброшены, и следующая итерация приведет к еще одному отсутствию кэша, доступу к памяти и так далее.Намного лучше сделать это:
Теперь, когда первое значение x загружается из памяти, следующие три также будут загружены в кэш (при условии подходящего выравнивания), что означает, что вам не нужно загружать какие-либо значения для следующих трех итераций.
Вышеупомянутый алгоритм может быть несколько улучшен за счет использования SIMD-инструкций, например, на архитектурах SSE2. Тем не менее, они работают намного лучше, если все значения непрерывны в памяти, и вы можете использовать одну инструкцию для загрузки четырех выборок вместе (больше в более поздних версиях SSE).
YMMV - проектируйте свои структуры данных в соответствии с вашим алгоритмом.
источник
double Sample::*
Часть является ключом!Позже вы можете получить доступ к этому члену в любом случае:
Обратите внимание, что вам нужен экземпляр для его вызова, поэтому он не работает как делегат.
Он используется редко, мне это нужно было, может быть, один или два раза за все мои годы.
Обычно лучше использовать интерфейс (например, чистый базовый класс в C ++).
источник
У IBM есть еще немного документации о том, как это использовать. Вкратце, вы используете указатель в качестве смещения в классе. Вы не можете использовать эти указатели отдельно от класса, к которому они относятся, поэтому:
Это кажется немного неясным, но одно из возможных приложений - это если вы пытаетесь написать код для десериализации общих данных во множество различных типов объектов, и ваш код должен обрабатывать типы объектов, о которых он абсолютно ничего не знает (например, ваш код в библиотеке, и объекты, в которые вы десериализуетесь, были созданы пользователем вашей библиотеки). Указатели на элементы дают вам общий, полуразборчивый способ обращения к отдельным смещениям элементов данных, не прибегая к бесчисленным трюкам void *, как вы могли бы использовать для структур Си.
источник
Это позволяет связывать переменные-члены и функции единообразно. Ниже приведен пример с вашим классом автомобилей. Более распространенное использование будет обязательным
std::pair::first
и::second
при использовании в алгоритмах STL и Boost на карте.источник
Вы можете использовать массив указателей на (однородные) данные членов, чтобы включить двойной интерфейс, именованный член (iexdata) и массив подстрочный (т. Е. X [idx]).
источник
union
типа to-pun не допускается стандартом, так как оно вызывает множество форм неопределенного поведения ... тогда как этот ответ в порядке.float *component[] = { &x, &y, &z }; return *component[idx];
Т.е. указатель на компонент, кажется, не имеет смысла, кроме запутывания.Один из способов, которым я воспользовался, - это если у меня есть две реализации того, как что-то сделать в классе, и я хочу выбрать одну во время выполнения без необходимости постоянно проходить оператор if, т.е.
Очевидно, что это практически полезно только в том случае, если вы чувствуете, что код достаточно забит, что оператор if замедляет выполнение действий, например. в глубине души какой-то интенсивный алгоритм где-то. Я все еще думаю, что это более элегантно, чем выражение if, даже в ситуациях, когда оно не имеет практического применения, но это всего лишь мое мнение.
источник
Algorithm
и двух производных классов, например,AlgorithmA
иAlgorithmB
. В таком случае оба алгоритма хорошо разделены и должны быть проверены независимо.Указатели на классы не являются настоящими указателями; класс является логической конструкцией и не имеет физического существования в памяти, однако, когда вы создаете указатель на член класса, он дает смещение в объект класса члена, где член может быть найден; Это дает важный вывод: поскольку статические члены не связаны с каким-либо объектом, указатель на член НЕ МОЖЕТ указывать на статический член (данные или функции). Рассмотрим следующее:
Источник: Полный справочник C ++ - Герберт Шильдт, 4-е издание
источник
Я думаю, что вы захотите сделать это, только если данные члена были довольно большими (например, объект другого довольно здоровенного класса), и у вас есть некоторая внешняя подпрограмма, которая работает только со ссылками на объекты этого класса. Вы не хотите копировать объект-член, так что это позволяет вам передавать его.
источник
Вот пример, где может быть полезен указатель на члены данных:
источник
Предположим, у вас есть структура. Внутри этой структуры есть * какое-то имя * две переменные одного типа, но с разным значением
Хорошо, теперь допустим, у вас есть куча
foo
s в контейнере:Хорошо, теперь предположим, что вы загружаете данные из отдельных источников, но данные представлены таким же образом (например, вам нужен тот же метод синтаксического анализа).
Вы могли бы сделать что-то вроде этого:
В этот момент вызов
readValues()
возвратит контейнер с унисонами «input-a» и «input-b»; все ключи будут присутствовать, и у foos есть либо a, либо b, либо оба.источник
Просто добавьте несколько вариантов использования для ответа @ anon's & @ Oktalist, вот отличный материал для чтения о указателе на функцию-член и указатель на член-данные.
https://www.dre.vanderbilt.edu/~schmidt/PDF/C++-ptmf4.pdf
источник