Косвенный вызов неконстантной функции для константного объекта - PullRequest
3 голосов
/ 20 декабря 2009

С учетом следующего кода:

class foo;

foo* instance = NULL;

class foo
{
public:
   explicit foo(int j)
    : i(j)
   {
      instance = this;
   }

   void inc()
   {
      ++i;
   }

private:
   int i;
};

Использует ли следующее определенное поведение?

const foo f(0);

int main()
{
   instance->inc();
}

Я спрашиваю, потому что я использую реестр классов, и поскольку я не изменяю напрямую f, было бы неплохо сделать его const, но позже f будет изменен косвенно реестр.

РЕДАКТИРОВАТЬ: Под определенным поведением я подразумеваю: Помещен ли объект в какое-то специальное место в памяти, которое может быть записано только один раз? О доступной только для чтения памяти не может быть и речи, по крайней мере, до constexpr C ++ 1x. Например, константные примитивные типы (часто) помещаются в постоянную память, и выполнение const_cast для нее может привести к неопределенному поведению, например:

int main()
{
    const int i = 42;
    const_cast<int&>(i) = 0; // UB
}

Ответы [ 7 ]

3 голосов
/ 20 декабря 2009

Да, это неопределенное поведение, согласно 7.1.5.1/4:

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

Обратите внимание, что время жизни объекта начинается после завершения вызова конструктора (3.8 / 1).

2 голосов
/ 20 декабря 2009

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

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

Обычно вы управляете такой ситуацией, определяя класс с помощью частного ctor, поэтому большинство клиентов не могут создавать объекты этого типа. Затем класс объявит класс владельца как друга, поэтому он может использовать закрытый ctor и / или статическую функцию-член для создания экземпляров (или часто только одного экземпляра) объекта. Затем класс владельца передает указатели (или ссылки) на const-объекты для использования клиентами. Вам не нужно ни изменяемого члена, ни отбрасывать константность, потому что владелец, который имеет «право» изменять объект, всегда имеет неконстантный указатель (или, опять же, ссылку) на объект. Его клиенты получают только постоянные указатели / ссылки, предотвращающие изменение.

1 голос
/ 20 декабря 2009

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

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

Просто вызов метода, опять же, совершенно законно.

1 голос
/ 20 декабря 2009

Это может быть один из редких случаев, когда можно использовать не очень известное ключевое слово mutable:

mutable int i;

i теперь можно изменить, даже если объект постоянный. Используется, когда логически объект не меняется, но на самом деле он меняется.


Например:

class SomeClass
{
// ....
    void DoSomething() { mMutex.lock(); ...; }
    mutable Mutex mMutex;
}

В DoSomething() объект не изменяется логически, и все же mMutex должен измениться, чтобы заблокировать его. Поэтому имеет смысл сделать его mutable, иначе ни один экземпляр SomeClass не может быть const (при условии, что вы блокируете muetx для каждой операции).

0 голосов
/ 20 декабря 2009

В GCC, по крайней мере, ваш конструктор должен быть explicit foo(int j) со словом int.

Однако прекрасно иметь два указателя на одно и то же значение: один const, а другой - нет.

0 голосов
/ 20 декабря 2009

Трудно сказать намерение с этими произвольными именами. Если i задуман как простой счетчик использования, и он на самом деле не считается частью данных, тогда вполне уместно объявить его как mutable int i;. Тогда const -ность экземпляра не нарушается, когда i изменено. С другой стороны, если i - это значимые данные в моделируемом пространстве, то это было бы очень плохо.

Отдельно от этого, тем не менее, ваш пример немного беспорядок для того, о чем вы, похоже, спрашиваете. foo* instance = NULL; эффективно (если сбивает с толку) использует NULL в качестве числового нуля и инициализирует instance, который не является const; затем вы отдельно инициализируете f, то есть const, но никогда не ссылаетесь на него.

0 голосов
/ 20 декабря 2009

Почему вы не используете const cast?

Есть ли причина сделать объект постоянным, хотя его состояние не является постоянным?

Также внесите следующие изменения:

explicit foo(int j = 0)    : i(j)   

{    instance = this;   }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...