Почему мы можем получить доступ к закрытым данным членов класса с помощью указателей, не используя членов friend function других членов класса?

#c #class #c 11 #pointers #private

#c #класс #c 11 #указатели #Частное

Вопрос:

Как вы знаете, к закрытым членам могут быть доступны только другие члены класса

 class DateClass // members are private by default
{
    int m_month; // private, can only be accessed by other members
    int m_day; // private, can only be accessed by other members
    int m_year; // private, can only be accessed by other members
};
 
int main()
{
    DateClass date;
    date.m_month = 12; // error
    date.m_day = 15; // error
    date.m_year = 2020; // error
 
    return 0;
}
 

Но я был весьма удивлен, что вы можете использовать указатели для доступа к закрытым членам данных. Пример:

 #include <iostream>  
  
class Test { 
private: 
    int data; 
  
public: 
    Test()
    {
        data = 0;
    }

    int getData()
    {
        return data;
    }
}; 
  
int main() 
{ 
    Test t; 
    int* ptr = (int*)amp;t; 
    *ptr = 10; 
    std::cout << t.getData(); // it will return 10
    return 0; 
}
 

Так это намеренно? Почему к закрытым членам можно получить доступ с помощью указателей? Я новичок, поэтому этот вопрос довольно глупый.

Комментарии:

1. Обычно вы можете получить доступ к любому адресу памяти во всем процессе. Идея private состоит в том, чтобы ограничить ваш доступ во время компиляции , чтобы защитить себя, а также других разработчиков , использующих ваш код. Предотвращение доступа на чтение / запись обычно устанавливается на уровне HW (MMU и т. Д.).

Ответ №1:

На самом деле поведение использования указателя, полученного с помощью инструкции

 int* ptr = (int*)amp;t; 
 

не определено. (Технически это строгое нарушение псевдонимов).

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

Комментарии:

1. Из любопытства, как class.mem/20 не применяется в случае OPs, где макет стандартный, а первый член действительно int есть? Я не понимаю нарушения sa, и мне искренне любопытно.

2. @WhozCraig: действительно, это класс стандартной компоновки (обратите внимание, что все члены имеют одинаковый доступ), поэтому адрес t совпадает с адресом data внутри него t . Но типы по-прежнему не связаны, поэтому применяется строгое наложение псевдонимов.

3. Как я понимаю из class.mem#general-note-12 , указатель можно преобразовать , из expr.static.cast #13 , использование static_cast from void* было бы хорошо, так int* ptr = static_cast<int*>(static_cast<void*>(amp;t)) что все будет хорошо. В качестве альтернативы, std::launder тоже должно быть хорошо.