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

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

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*)&t; 
    *ptr = 10; 
    std::cout << t.getData(); // it will return 10
    return 0; 
}

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

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

goodvibration 15.12.2020 12:36
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
338
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

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

int* ptr = (int*)&t; 

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

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

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

WhozCraig 15.12.2020 12:55

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

Bathsheba 15.12.2020 13:19

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

Jarod42 15.12.2020 16:46

Другие вопросы по теме