Потому что, как говорит Бьерн, C++ предназначен для защиты от Мерфи, а не от Макиавелли.
Другими словами, предполагается, что он защищает вас от несчастных случаев, но если вы начнете что-то делать, чтобы разрушить его (например, использовать гипс), он даже не попытается вас остановить.
Когда я думаю об этом, я имею в виду несколько иную аналогию: это как замок на двери ванной. Это дает вам предупреждение о том, что вы, вероятно, не хотите сейчас входить туда, но если вы решите открыть дверь снаружи, это тривиально.
Редактировать: что касается вопроса, который обсуждает @Xeo, о том, почему в стандарте говорится «иметь одинаковый контроль доступа», а не «иметь весь общедоступный контроль доступа», ответ длинный и немного запутанный.
Давайте вернемся к началу и рассмотрим такую структуру, как:
struct X {
int a;
int b;
};
C всегда имел несколько правил для такой структуры. Во-первых, в экземпляре структуры адрес самой структуры должен быть равен адресу a
, поэтому вы можете привести указатель на структуру к указателю на int
и получить доступ к a
с четко определенными результатами. Другая заключается в том, что элементы должны располагаться в памяти в том же порядке, в котором они определены в структуре (хотя компилятор может вставлять между ними отступы).
Для C++ было намерение сохранить это, особенно для существующих структур C. В то же время имелось очевидное намерение, что если компилятор захочет применить private
(и protected
) во время выполнения, это должно быть легко сделать (достаточно эффективно).
Следовательно, учитывая что-то вроде:
struct Y {
int a;
int b;
private:
int c;
int d;
public:
int e;
// code to use `c` and `d` goes here.
};
Компилятор должен поддерживать те же правила, что и C, в отношении Y.a
и Y.b
. В то же время, если он собирается обеспечить доступ во время выполнения, он может захотеть переместить все общедоступные переменные вместе в память, поэтому макет будет больше похож на:
struct Z {
int a;
int b;
int e;
private:
int c;
int d;
// code to use `c` and `d` goes here.
};
Затем, когда он выполняет действия во время выполнения, он может в основном делать что-то вроде if (offset > 3 * sizeof(int)) access_violation();
Насколько мне известно, никто никогда этого не делал, и я не уверен, что остальная часть стандарта действительно разрешает это, но, похоже, в этом направлении существовали, по крайней мере, наполовину сформированные зародыши идеи.
Чтобы обеспечить выполнение обоих из них, C++98 сказал, что Y::a
и Y::b
должны быть в памяти в таком порядке, а Y::a
должно быть в начале структуры (т. е. правила, подобные C). Но из-за промежуточных спецификаторов доступа Y::c
и Y::e
больше не должны были быть упорядочены друг относительно друга. Другими словами, все последовательные переменные, определенные без спецификатора доступа между ними, были сгруппированы вместе, компилятор мог свободно переставлять эти группы (но все же должен был оставить первую в начале).
Это было нормально, пока какой-то придурок (например, я) не указал, что в способе написания правил есть еще одна небольшая проблема. Если бы я написал код вроде:
struct A {
int a;
public:
int b;
public:
int c;
public:
int d;
};
...в итоге вы немного противоречите себе. С одной стороны, это все еще официально была структура POD, поэтому должны были применяться правила, подобные C, но, поскольку у вас были (по общему признанию бессмысленные) спецификаторы доступа между членами, это также давало компилятору разрешение переупорядочивать члены, таким образом нарушая C-подобные правила, которые они намеревались.
Чтобы исправить это, они немного переформулировали стандарт, чтобы он говорил о том, что все члены имеют одинаковый доступ, а не о том, существует ли между ними спецификатор доступа. Да, они могли просто декретировать, что правила будут применяться только к представителям общественности, но, похоже, никто не видел в этом никакой выгоды. Учитывая, что это была модификация существующего стандарта с большим количеством кода, который использовался в течение достаточно долгого времени, они выбрали наименьшее изменение, которое они могли сделать, чтобы решить проблему.
person
Jerry Coffin
schedule
23.08.2012
class A { int a; public: int b; };
относительный порядокa
иb
не предписывается стандартом (у них разные спецификаторы доступа), поэтому*reinterpret_cast<int*>(&a) = 5;
может изменить либоa
, либоb
. - person David Rodríguez - dribeas   schedule 23.08.2012reinterpret_cast
применяются только к классам стандартного макета, и стандарт явно требует, чтобы класс был стандартным макетом для всех не- статические элементы данных должны иметь одинаковый контроль доступа, глава 9/7 (отсутствие виртуальных функций является еще одним требованием для стандартной компоновки, но достаточно нескольких спецификаторов контроля доступа, чтобы нарушить гарантии) - person David Rodríguez - dribeas   schedule 23.08.2012