Подтвердить что ты не робот

Почему статический доступ к элементам данных класса через указатели возвращает 1?

С учетом следующего кода:

#include <iostream>

using std::cout;

class A {
    public:
        virtual ~A() {}
        int x,y,z;
};

int main (void)
{
    std::cout<<&A::x;
    std::cout<<&A::y;
    std::cout<<&A::z;
}

Вывод:

111

В чем смысл вывода? Почему это 1? Есть ли веские основания для доступа к членам класса с помощью указателей (без созданного объекта)?

EDIT - Использование:

printf("%p",&A::x);
printf("%p",&A::y);
printf("%p",&A::z);

отпечатки: 4, 8 и C.

Теперь у меня больше смысла, я думаю.. (байты) Но все-таки, есть ли для этого какое-то использование?

4b9b3361

Ответ 1

Для T = &C::m нет operator<<(std::ostream&, T). Обычно вы получите сообщение об ошибке.

Но вместо этого существует один для T = bool и неявное преобразование из указателя-члена в bool. Таким образом, результат, который вы видите, является результатом того, что эти указатели не являются нулевыми (преобразуются в true).

Попробуйте это, например:

#include <iomanip>
#include <iostream>

struct A
{
    int x, y, z;
};

int main()
{
    std::cout << std::boolalpha; // print boolean values as text

    std::cout << &A::x << std::endl;
    std::cout << &A::y << std::endl;
    std::cout << &A::z << std::endl;
}

Вывод:

верно
правда
true


Обратите внимание, что в коде printf("%p", &A::X) у вас есть поведение undefined.

Значение для спецификатора %p должно быть void*, и нет преобразования от указателя-члена к void*. Вместо этого у вас есть псевдонимы (типа-каламбуры) до void*, которые являются undefined. (Предположим, что sizeof(&A::x) было 4, а sizeof(void*) было 64, ничто не говорит, что этого не может быть.)

Вам просто нужно понять, что не все указатели можно рассматривать как целочисленные смещения. То, что мы можем даже напечатать указатель, определяется реализацией: он может печатать "яблоко" для нулевого, "груши" для одного значения и "молоко" для другого, если захотела (немой) реализация. Я затронул эту разницу между значениями и их представлениями до.

И в этом случае выход для значения вообще отсутствует. И это хорошо, не все значения имеют значимые печатные выходы. Самое большее, что вы можете сделать, это распечатать отдельные биты:

#include <climits>
#include <iostream>
#include <type_traits>

template <typename T>
auto print_bits(const T& value)
    -> typename std::enable_if<std::is_standard_layout<T>::value>::type
{
    // it okay to alias a standard-layout type as a sequence of bytes:
    const auto valueAsBytes = reinterpret_cast<const unsigned char*>(&value);

    for (std::size_t byte = 0; byte < sizeof(T); ++byte)
    {
        // print in reverse order
        const std::size_t byteIndex = sizeof(T) - byte - 1;

        const unsigned char byteValue = valueAsBytes[byteIndex];

        for (std::size_t bit = 0; bit < CHAR_BIT; ++bit)
        {
            // print in reverse order
            const std::size_t bitIndex = CHAR_BIT - bit - 1;

            const bool bitValue = (byteValue & (1U << bitIndex)) != 0;

            std::cout << (bitValue ? 1 : 0);
        }

        std::cout << ' ';
    }

    std::cout << std::endl;
}

(Я печатаю байты и биты в обратном порядке, потому что на моих архитектурах это помещает младший бит справа. Я предпочитаю просматривать двоичные значения таким образом.)

Это дает:

struct A
{
    int x, y, z;
};

int main()
{
    // example:
    for (unsigned i = 0; i < 64; ++i)
        print_bits(i);

    std::cout << std::endl;

    // member-pointers:
    print_bits(&A::x);
    print_bits(&A::y);
    print_bits(&A::z);
}

Вывод:

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
00000000 00000000 00000000 00000010
[...]
00000000 00000000 00000000 00111101
00000000 00000000 00000000 00111110
00000000 00000000 00000000 00111111

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000100
00000000 00000000 00000000 00001000

Нет никаких гарантий того, что вы видите для указателей-членов.

Ответ 2

&A::x создает указатель на член класса A. Указатель на член является, в некотором роде, представлением "позиции" члена внутри класса. То, что эти цифры означают, точно зависит от конкретной реализации. Я бы предположил, что в вашей конкретной реализации это означает, что x находится в индексе 1 в классе (указатель таблицы виртуальной функции находится на индексе 0). EDIT Собственно, ответ GManNickG показывает Я ошибался в этом предположении и просто печатал указатель на элемент, неявно преобразованный в bool.

Обратите внимание, что указатель на элемент не указатель в классическом смысле. Вы не можете разыскивать указатель на элемент напрямую. Вам всегда нужен указатель/ссылка на объект, чтобы идти вместе с ним. Пример (с использованием вашего класса):

int main()
{
  int A::*pm;  // pm is a pointer to member of A of type int
  A a;
  pm = &A::x;
  std::cout << a.*pm; //will output a.x
  pm = &A::y;
  std::cout << a.*pm; //will output a.y
  A *pa = &a;
  std::cout << pa->*pm;  //will output pa->y
}