Почему С++ разрешает доступ к частному члену класса через указатель?

#include<iostream>
using namespace std;

class A
{
    int value;
 public:
    A(){value = 1;}
    ~A(){}

    void print(){cout << value << endl;}
 };

int main()
{
    A a;
    int* p = (int*)(&a);
    *p = 20;
    a.print();//output is 20.
}

Разве это не нарушает инкапсуляцию класса? Я новичок в С++. Я никогда не видел этот метод, который может получить доступ к частному члену класса в книге «С++ учебник».


person binbin    schedule 06.04.2015    source источник
comment
Связано: stackoverflow.com/q/424104/3093378   -  person vsoftco    schedule 06.04.2015


Ответы (2)


Это разрешено, поскольку A — это класс standard_layout, совместимый с с макетом других языков, что означает, что его можно передавать функциям, которые были написаны на чем-то другом, кроме C++. Это нарушает инкапсуляцию, но это не то, что вы обычно хотите делать в стране C++, это функция совместимости.

Однако, поскольку вы новичок, вы почти наверняка захотите избежать всего, что связано с использованием приведений в стиле c / reinterpret_cast. Доверяйте системе типов и элементам управления доступом, они здесь, чтобы помочь вам - если вы переопределите их, вам нужно будет точно знать, почему, что, очевидно, не имело место здесь.

person user657267    schedule 06.04.2015
comment
Спасибо за разъяснение моего теперь удаленного неправильного ответа, сегодня я определенно кое-чему научился. Однако с virtual все ставки сняты. - person vsoftco; 06.04.2015

Ваш код по сути является неопределенным поведением. По следующим строкам:

int* p = (int*)(&a);
*p = 20;

вы разыменовываете указатель с каламбуром, несмотря на то, что такой каламбур не соблюдает строгие правила псевдонимов. Чтобы получить правильную версию кода, A должен быть агрегатным типом, см. раздел Псевдонимы типов здесь. В вашем коде A практически не является агрегатным типом, так как содержит приватный член. См. определение агрегатного типа здесь.

person Lingxi    schedule 06.04.2015
comment
В качестве альтернативы, a.value и *p относятся к одному и тому же типу (int), поэтому соблюдаются строгие правила присвоения псевдонимов. - person Ben Voigt; 06.04.2015
comment
Что касается 4-го пункта, A должен быть агрегатным типом. Но это не так. Тот факт, что a.value и *p относятся к одному и тому же типу int, ничего не меняет. См. 5-й комментарий к принятому ответу в stackoverflow.com /вопросы/29298508/. - person Lingxi; 06.04.2015
comment
Извините за путаные комментарии ранее. Строгое правило алиасинга здесь не применяется. Из [class.mem]/20 мы узнаем, что If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member., т. е. компилятор должен предположить, что адрес может относиться как к int, так и к A. - person user657267; 07.04.2015
comment
@user657267 user657267 Указатели разных типов, относящиеся к одному и тому же местоположению. Это называется каламбуром типа и именно тогда применяются строгие правила псевдонимов. - person Lingxi; 07.04.2015
comment
@Lingxi За исключением этого случая, когда в стандарте специально указано исключение. Если бы приведенный выше код нарушал строгое использование псевдонимов, это сделало бы бессмысленным определение всей концепции класса стандартной компоновки. - person user657267; 07.04.2015