LINUX.ORG.RU

С++ размышление об указателях на члены-переменные структур/классов

 


0

1

Добрый день. Нормально ли считается использовать обычные указатели на члены-переменные структур/классов, например так:

#include <iostream>

using namespace std;

struct A {
  int i;
};

void f(int *i) { cout << dec << *i << " " << hex << (long)i << endl; }
void f(int &i) { cout << dec << i << " " << hex << (long)&i << endl; }

void f(A *pa, int A::*am) {
  cout << dec << pa->*am << " " << hex << (long)&(pa->*am) << endl;
}

void f(A *pa) { cout << dec << pa->i << " " << hex << (long)&(pa->i) << endl; }
void f(A &ra) { cout << dec << ra.i << " " << hex << (long)&(ra.i) << endl; }

int main() {
  A a{55};

  // Номально ли делать так
  f(&a.i);
  f(a.i);

  // Или лучше так (чем лучше?)
  f(&a, &A::i);

  // Или может вообще лучше так
  f(&a);
  f(a);

  return 0;
}

// Вывод:
// 55 7ffcb727e274
// 55 7ffcb727e274
// 55 7ffcb727e274
// 55 7ffcb727e274
// 55 7ffcb727e274
★★★★★

Конечно. А указатель на поле может понадобится для обхода массива каких-то структур, например, или в какой-то шаблонной обёртке доступа к полю. Одно другому не является полной заменой.

xaizek ★★★★★ ()
Ответ на: комментарий от xaizek

А если сравнивать

f(A *pa, int A::*am);

// и

void f(int *i);
void f(int &i);

Что лучше? Не вижу ни одного преимущества первой формы (понимаю, когда у нас есть указатель на не статическую функцию-член, но в случае члена переменной где тут удобство).

rumgot ★★★★★ ()
Последнее исправление: rumgot (всего исправлений: 1)

Конечно же ссылка:

void f(int &i)  
f(a.i);

f(&a, &A::i);

Это извращение.

f(a);

Если нужна функция для распечатывания значения класса, то лучше засунуть её во внутрь класса в виде метода

ox55ff ★★★ ()
Последнее исправление: ox55ff (всего исправлений: 1)

Смысл вопроса непонятен.

Обычный указатель (скажем на int) вы используете когда вам нужно что-то сделать с каким-то int-ом и неважно каким именно. Лежит ли этот int как поле в какой-то структуре или это элемент вектора int-ов, или это одиночный int на стеке – без разницы. У вас есть просто операция f(int *p) которая что-то делает с любым int-ом. Ну, скажем:

void ensure_not_negative(int *p) {
  if(*p < 0) *p = 0;
}

Указатель на член класса используется когда вам нужно производить какие-то операции не над произвольным int-ом, а над int-ом, который является членом конкретного класса. При этом вы не знаете, с каким именно экземпляром класса вам придется работать. Но вам важно, что работать придется именно с членом этого класса. Например:

struct Point {
  int x_;
  int y_;
};

void ensure_not_negative(Point * instance, int Point::*member) {
  if(instance->*member < 0) instance->*member = 0;
}
...
std::vector<Point> points{...};
for(auto & p : points) {
  ensure_not_negative(&p, &Point::x_);
  ...
  ensure_not_negative(&p, &Point::y_);
}

Ну и да, представление указателей на члены у разных компиляторов разное и не факт, что указатель на член может быть скастован в void* (и тем более в long).

eao197 ★★★★★ ()

Нормально ли считается использовать обычные указатели на члены-переменные структур/классов

Да, вполне нормально, если это не нарушает инкапсуляцию.

DELIRIUM ☆☆☆☆☆ ()

Как говорили выше, в целом всё ок. Но нужно учитывать, что это может сломаться при использовании указателей на структуры с атрибутом packed. По-крайней gcc об этом предупреждает.

Если структура упакована, то выравнивание её членов может быть произвольным, а функции рассчитывают, что принимаемые аргументы будут нормально выравнены.

Ivan_qrt ★★★★★ ()
Ответ на: комментарий от Ivan_qrt

Но нужно учитывать, что это может сломаться при использовании указателей на структуры с атрибутом packed.

атрибут packed - не стандарт + это не точно, но указатель на член класса тоже может сломаться в таком случае

next_time ★★★★★ ()
Ответ на: комментарий от invy

нет ни каких особых проблем с дизайном, наоборот

  1. в С++ считается предпочтительным передавать всё по ссылке, а не по значению, а так как согласно ООП всё лежит в объектах, то передача ссылок на члены-переменные структур/классов является общепринятой нормой, отступление от которой скорее является странностью

  2. в языках, произошедших от С++ (java, C#) - вообще всё, кроме примитивных типов, передаётся именно по указателю

единственное отступление от общепринятой практики у ТС лишь в том, что в С++ он передаёт адрес по указателю, а т.к. указатель, передаваемый в функцию, с т.з. вызывющего кода всегда константный, разница лишь в том, что передаваемое значение может быть nullptr, что иногда удобно

next_time ★★★★★ ()
Ответ на: комментарий от next_time

атрибут packed - не стандарт + это не точно, но указатель на член класса тоже может сломаться в таком случае

Если в такой форме

f(A *pa, int A::*am);

то тоже может сломаться?

rumgot ★★★★★ ()
Последнее исправление: rumgot (всего исправлений: 2)
Ответ на: комментарий от next_time

единственное отступление от общепринятой практики у ТС лишь в том, что в С++ он передаёт адрес по указателю, а т.к. указатель, передаваемый в функцию, с т.з. вызывющего кода всегда константный, разница лишь в том, что передаваемое значение может быть nullptr, что иногда удобно

Можешь пожалуйста подробней? Не распарсил.

rumgot ★★★★★ ()
Ответ на: комментарий от next_time

А, ты в том смысле, что в случае указателя можно еще nullptr передать при необходимости, а в случае ссылки там обязана быть конкретная сущность?

rumgot ★★★★★ ()
Последнее исправление: rumgot (всего исправлений: 2)
Ответ на: комментарий от DllMain

И что? Ты нашел один пост четырехлетней! давности и сделал вывод о фиксации? Да ты видимо профессиональный психолог. И это ж, серьезно, надо было среди тучи моих постов отрыть :)

Эту энергию бы да в мирное русло, динамо машину там крутить и снабжать энергией город, например.

curufinwe ★★★★★ ()
Последнее исправление: curufinwe (всего исправлений: 2)
Ответ на: комментарий от curufinwe

Оправдывайся теперь)

И это ж, серьезно, надо было среди тучи моих постов отрыть :)

Ты серьёзно думаешь, что я целенаправлено искал что-то подобное, а не наткнулся случайно?

DllMain ()
Ответ на: комментарий от DllMain

Оправдывайся теперь)

От чего? Я и сейчас могу сказать, что против царя я ничего не имею. А его срачи зачастую заставляют задуматься об очень интересных вещах. Но я не захожу в каждую тему и не пишу «а царь то, а царь сё», как человек, которого я призывал задуматься.

Ты серьёзно думаешь, что я целенаправлено искал что-то подобное, а не наткнулся случайно?

Нет, это была шутка.

curufinwe ★★★★★ ()
Ответ на: комментарий от curufinwe

А его срачи зачастую заставляют задуматься об очень интересных вещах

Например?

Но я не захожу в каждую тему и не пишу «а царь то, а царь сё», как человек, которого я призывал задуматься

Крутчмастер грешит этим, да. Но это скорее юморок такой.

А вообще - забей, мне просто показалось забавным твоё сообщение в том топике, если рассматривать его уже после прочтения ответа крутчмастеру. Больше ничего

DllMain ()
Ответ на: комментарий от DllMain

Например?

Например - как писать программы, как разные парадигмы программирования соотносятся между собой, какие подходы эффективнее и лучше. Еще раз - заставляет задуматься об этом. Не больше и не меньше.

curufinwe ★★★★★ ()