2010-02-28 2 views
5

Я изучаю «Язык программирования на С ++» из Bjarne Stroustrup, и он рассказывает о логической и физической константе класса.Физическая констатация класса

Пример логической константность что-то вроде:

class A { 
    int m; 
    void func() const { m++; } //forbidden 
} 

Это можно обойти это с броском, как:

class A { 
    int m; 
    void func() const { (A*) this)->m++; } //allowed 
} 

По его словам, логичной константность является

"объект, который остается постоянным для пользователей".

и физическое константность является

«хранятся в памяти только для чтения»

В записке он говорит, что

физическое константность может быть обеспечено путем размещения объект в постоянной памяти только для классов без конструкторов

Я не совсем понял это утверждение. Может ли кто-нибудь дать объяснение о том, как применять физическую константу и почему она не работает, если класс имеет конструктор?

ответ

13

Вы получили несколько ответов уже, но я считаю, что большинство из них (если не все) пропустят точку.

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

  1. Оборудование/уровень физической ОС константность
  2. уровень
  3. Язык физической константность
  4. Логическое константность (также, конечно, уровень языка)

Физическая констатация оборудования/уровня ОС - это физическая константа, которую, как представляется, описывают другие ответы. Это происходит, когда объект помещается в память, защищенную от записи: память только для чтения (RO). Защита может быть реализована с помощью аппаратных средств или средств, предоставляемых ОС, или обоих. Однако сам язык C++ не разделяет этот тип константы на отличительную категорию. Язык C++ не относится к таким вопросам низкого уровня. Когда понятие физической консистенции возникает в контексте C++, оно обычно относится к следующему виду константы.

Языковой уровень физического состояния. Эта константа имеет место просто, когда вы объявляете объект как const. Следующие объектами являются физическими константами с точки зрения языка C++

const double d = 5; 
const int i = 42; 
const std::string str = "Hello World!"; 
const MyClass c; 

Обратите внимание, что это на самом деле не имеет значения, является ли этот объект действительно помещается в памяти RO или нет. Язык говорит, что любые попытки изменить эти объекты приведут к Undefined Behavior (UB), независимо от того, является ли память RO или нет. Обратите также внимание на то, что если вы попытаетесь изменить эти объекты, проявления этого UB не ограничиваются простой сбой программы (если они действительно находятся в памяти RO). Например, компилятор может предположить, что эти объекты никогда не меняются и могут оптимизировать код в этом предположении, исключая доступ к этим объектам в ситуациях, когда это кажется ненужным.Из-за этого даже если память, занятая этими объектами, может быть записана, и даже если вам удастся «успешно» их каким-либо образом изменить, ваш код может по-прежнему вести себя так, как если бы ваши модификации никогда не происходили. UB - UB. Все может случиться.

С языковой точки зрения, этот тип созвездия, конечно же, предназначен для включения 1-го рода.

И, наконец, до Логическая константа. Логическая константа в C++ - это константа так называемого пути доступа к объекту. Путь доступа - это ссылка или указатель, который позволяет косвенно обращаться к существующему объекту. Рассмотрите эту декларацию

const MyClass* pc; 

Это указатель на тип const MyClass. Обратите внимание, однако: это на самом деле не означает, что фактический объект, на который указывает этот указатель, является константой. Это просто позволит вам «видеть» его как константу. Объект может легко быть либо постоянным

const MyClass c; 
pc = &c; 

или это может быть непостоянной

MyClass nc; 
pc = &nc; 

Другими словами, имея только этот указатель p у вас есть путь в постоянная accss к некоторому объекту типа MyClass. Вы не знаете и (обычно) не должны знать, действительно ли этот объект является константой. Поскольку путь доступа, который был предоставлен вам, является постоянным, вы должны рассматривать этот объект как константу. Конечно, если вы как-то знаете, что объект на другом конце этого пути доступа не является постоянной, вы можете законно откинуть константность пути

MyClass* p = const_cast<MyClass*>(pc); 

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

Обратите внимание, что пример в исходном посте говорит именно об этом. Когда вы объявляете метод класса A как const, это просто означает, что неявный параметр this, переданный этому методу, будет иметь тип const A*, то есть он обеспечит постоянный путь доступа к объекту A. Это то, что я описал выше как логическая константа. Обратите внимание, что если объект был объявлен как, например, const A a;, это физическая константа уровня языка и его изменение, как показано в примере, это незаконно, независимо от того, находится ли объект в памяти RO или нет.

Теперь, чтобы обеспечить одну заключительную иллюстрацию выше, рассмотрим follwing декларации

const MyClass* const* const* const p = /* whatever */; 

Эта декларация имеет 4 const классификаторов в нем. Один из этих квалификаторов имеет большое качественное отличие от других. Это самый правый. Крайний правый constобъявляет физический константность объекта p (константность самого указателя), в то время как остальные const отборочных объявить логической константности объектов, которые они будут указывающими на (константность пути доступа).

Опять же, я считаю, что в его книге «Страуструп» говорится о различии между 2-м и 3-м понятиями константы, а не о 1-м, поскольку язык С ++ на самом деле не разделяет 1-й от 2-го. Обратите внимание, что в этом примере говорится, что модификация путем отбрасывания константы «разрешена», в то время как в спецификации языка четко указано, что изменение констант 2-го рода с помощью этого подхода немедленно является незаконным.

+0

+1: Хороший ответ. – Arun

+0

Раньше я нашел эту страницу [http://www.parashift.com/c++-faq-lite/const-correctness.html] полезной в контексте const-correctness. – Arun

+0

Чрезвычайно хорошо написанный и подробный. –

5

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

Физический const работает только для объектов, которые могут быть инициализированы во время компиляции и, следовательно, не нуждаются в конструкторе.

Создание памяти readonly не является чем-то, что вы можете сделать явно на C++, но у многих компиляторов есть расширения, которые позволяют вам это делать, и большинство современных компиляторов помещают код и некоторые данные в память только для чтения, если архитектура ЦП позволяет это.

В MSVC, вы можете принудительно инициализацию глобальную переменную, которая должна храниться в памяти только для чтения, поместив его в section помеченный как читать, но не писать, как этот

#pragma section("rosec",read) 
__declspec(allocate("rosec")) int j = 0; // this will be in a readonly data segment. 
+0

Это не объясняет, как обеспечить соблюдение физических констант. Он упоминает только одно требование в этом процессе. – pestilence669

+0

@Pestilence: добавлена ​​информация о форсировании выделения в readonly data seg. –

+0

Технически физическая константа * может быть применена даже к конструкторам. Среда выполнения может отмечать страницу памяти, в которой объект находится как доступный только для чтения, только после инициализации объекта. Конечно, было бы довольно глупо делать это (так как вся страница памяти (обычно 4 КБ) была бы заблокирована, но она могла бы быть сделана – jalf

3

Вам нужна помощь от эксплуатации будет система.

В Linux вы можете вызвать mprotect для отображения страницы только для чтения. В Windows есть соответствующий API.

Это зависит от вас, чтобы придумать распределитель, который беспокоится о границах страниц и границах объектов.

Если у вашего объекта есть конструктор, он не может быть помещен в раздел только для чтения компоновщиком, так как это только для чтения остановит конструктор от конструирования.

Если у вас есть класс POD, и вы объявляете объект в области видимости файла const, он может оказаться в области только для чтения.

2

Для определенных типов объектов компилятор будет строить объект во время компиляции и поместить его в постоянное запоминающее устройство. В результате этого во время компиляции это может произойти только в том случае, если нет работы во время выполнения, поэтому вы не можете иметь конструкторы.

struct A 
{ 
    int x; 
    float y; 
}; 

A const a = {5,6.5}; // This object can be built at compile time and placed in read only memory. 
+0

Но что произойдет, если я напишу это: (const cast (a)) .x = 7; –

+0

Я помню, как это тестировалось раньше, и помню, что я могу изменить значение памяти для целых чисел const/ggc. Я понимаю, что только данные, находящиеся в сегменте кода, доступны только для чтения на современных архитектурах. Подстановка компилятора будет корректной, но операции с указателями все равно не удастся. – pestilence669

+0

@Pestilence: Код, конечно, только для чтения, но данные могут быть также. Строковые литералы обычно также являются readonly. –

0

Чтобы действительно применять физическую константность, вы всегда можете память карта защищенный от записи USB-диск и/или CD-R. Это позволит вам создавать указатели на ваши физически экземпляры const. Оверкилл, но я думал, что упомянул об этом.

Мое понимание и опыт в том, что только данные в вашем сегменте кода (код исполняемого машинного кода) доступны только для чтения на современных архитектурах (если это не отключено, для самомодифицирующегося кода/типа JVM). Ваш компилятор будет стремиться обманывать, перелистывать между буквальной заменой в сегменте кода и полагаться и читать/писать ОЗУ для операций указателя (всегда переопределяется типом).

Таким образом, одно из предложений до сих пор упоминает маркировку страниц памяти вручную через вашу операционную систему. Вероятно, это ваш единственный жизнеспособный вариант для промышленной силы. Это или мое предложение об оборудовании. Ваш компилятор не сделает этого для вас.

Смежные вопросы