Если изменение объекта константы является неопределенным поведением тогда, как конструкторы и деструкторы действуют с доступом для записи?

В стандарте C++ говорится, что изменение объекта первоначально объявило const неопределенное поведение. Но тогда как конструкторы и деструкторы действуют?

class Class {
public:
    Class() { Change(); }
    ~Class() { Change(); }
    void Change() { data = 0; }
private:
    int data;
};

//later:
const Class object;
//object.Change(); - won't compile
const_cast<Class&>( object ).Change();// compiles, but it's undefined behavior

Я подразумеваю здесь, что конструктор и деструктор делают точно то же самое как код вызова, но им позволяют изменить объект, и вызывающей стороне не разрешают - он сталкивается с неопределенным поведением.

Как это, как предполагается, работает при реализации и согласно стандарту?

9
задан sharptooth 16 February 2010 в 06:27
поделиться

5 ответов

Стандарт явно позволяет конструкторам и деструкторам иметь дело с объектами const . из 12.1 / 4 «Конструкторы»:

Конструктор может быть вызван для объекта const , volatile или const volatile объекта. ... Семантика const и volatile (7.1.5.1) не применяется к строящемуся объекту. Такая семантика вступает в силу только после завершения работы конструктора наиболее производного объекта (1.8).

И 12.4/2 «Деструкторы»:

Деструктор может быть вызван для объекта const , volatile или const volatile объекта. ... Семантика const и volatile (7.1.5.1) не применяется к уничтожаемому объекту. Такая семантика перестает действовать после запуска деструктора самого производного объекта (1.8).

В качестве фона Страуструп говорит в «Проектировании и развитии C ++» (13.3.2 Уточнение определения const ):

Чтобы гарантировать, что некоторые, но не все, const объекты могут быть размещены в постоянном запоминающем устройстве (ПЗУ), я принял правило, согласно которому любой объект, имеющий конструктор (т. Е. Требуемую инициализацию среды выполнения), не может быть помещен в ПЗУ, но другие const объекты могут.

...

Объявленный объект const считается неизменным с момента завершения конструктора до начала его деструктора. Результат записи в объект между этими точками считается неопределенным.

При первоначальной разработке const я помню, что утверждал, что идеальным const был бы объект, который доступен для записи до тех пор, пока конструктор не запустится, а затем станет доступным только для чтения с помощью некоторой аппаратной магии, и, наконец, при входе в деструктор снова становится доступным для записи. Можно представить себе архитектуру с тегами, которая действительно работает таким образом. Такая реализация вызвала бы ошибку времени выполнения, если бы кто-то мог писать в объект, определенный const .С другой стороны, кто-то может писать в объект, не определенный const , который был передан как ссылка или указатель const . В обоих случаях пользователю придется сначала отбросить const . Смысл этого представления состоит в том, что отбрасывание const для объекта, который был изначально определен const , а затем запись в него, в лучшем случае undefined, тогда как то же самое для объекта, который не был ' Первоначально определенный t const является допустимым и хорошо определенным.

Обратите внимание, что при таком уточнении правил значение const не зависит от того, имеет ли тип конструктор или нет; в принципе все делают. Любой объект, объявленный const теперь, может быть помещен в ПЗУ, помещен в сегменты кода, защищен контролем доступа и т. Д., Чтобы гарантировать, что он не изменится после получения своего начального значения. Однако такая защита не требуется, поскольку современные системы не могут защитить каждую const от всех форм коррупции.

15
ответ дан 4 December 2019 в 11:04
поделиться

Вот способ, которым игнорирование стандарта может привести к неправильному поведению. Рассмотрим ситуацию, подобную этой:

class Value
{
    int value;

public: 
    value(int initial_value = 0)
        : value(initial_value)
    {
    }

    void set(int new_value)
    {
        value = new_value;
    }

    int get() const
    {
        return value;
    }
}

void cheat(const Value &v);

int doit()
{
    const Value v(5);

    cheat(v);

    return v.get();
}

При оптимизации компилятор знает, что v является константой, поэтому может заменить вызов v.get () на 5 .

Но, скажем, в другой единице перевода вы определили cheat () следующим образом:

void cheat(const Value &cv)
{
     Value &v = const_cast<Value &>(cv);
     v.set(v.get() + 2);
}

Таким образом, хотя на большинстве платформ это будет работать, его поведение может измениться в зависимости от того, что делает оптимизатор. .

1
ответ дан 4 December 2019 в 11:04
поделиться

вот как я его использую:

function customRange(input)
{
    var min = new Date();
    return {
        minDate: ((input.id == "txtStartDate") ? min : (input.id == "txtEndDate" ? $("#txtStartDate").datepicker("getDate") : null)),
        maxDate: (input.id == "txtStartDate" ? $("#txtEndDate").datepicker("getDate") : null)
    };
}
-121--1041251-

Подробнее о том, что сказал Джерри Коффин: стандарт делает доступ к объекту const неопределенным, только если этот доступ происходит в течение жизни объекта.

7,1,5,1/4:

За исключением того, что любой член класса, объявленный изменяемым (7,1,1), может быть изменен, любая попытка изменить объект const в течение его жизни (3,8) приводит к неопределенному поведению.

Время жизни объекта начинается только после завершения конструктора.

3,8/1:

Время жизни объекта типа T начинается, когда получается:

  • место хранения с надлежащим выравниванием и размером для типа T, и
  • , если T является типом класса с нетривиальным конструктором (12,1), вызов конструктора завершен.
2
ответ дан 4 December 2019 в 11:04
поделиться

В стандарте не очень много говорится о том, как реализация заставляет это работать, но основная идея довольно проста: const применяется к объекту, а не (обязательно) к памяти, в которой хранится объект. Поскольку ctor является частью того, что создает объект, он не является объектом до тех пор, пока (через некоторое время после) ctor не вернется. Аналогично, поскольку dtor принимает участие в уничтожении объекта, он также больше не работает с полным объектом.

1
ответ дан 4 December 2019 в 11:04
поделиться

Константность для определяемого пользователем типа отличается от константности для встроенного типа. Константность при использовании с пользовательскими типами называется "логической константностью". Компилятор следит за тем, чтобы на const-объекте (или указателе, или ссылке) можно было вызывать только функции-члены, объявленные как "const". Компилятор не может выделить объект в некоторой области памяти, доступной только для чтения, потому что non-const функции-члены должны иметь возможность изменять состояние объекта (и даже const функции-члены должны иметь такую возможность, если переменная-член объявлена mutable).

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

0
ответ дан 4 December 2019 в 11:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: