2009-12-19 2 views
3

Учитывая следующий код:Косвенно вызов неконстантных функций на константном объекте

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 модифицируется косвенно в реестре.

EDIT: Определенное поведение Я имею в виду: Является ли объект помещен в какую-либо специальную ячейку памяти, которая может быть записана только один раз? Память только для чтения не может быть и речи, по крайней мере до constexpr из C++ 1x. Постоянные примитивные типы, например, являются (часто) помещается в память только для чтения, и делать const_cast на него может привести к непредсказуемому поведению, например:

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

Несомненно, линия instance = this; не будет компилироваться, так как вы назначаете указатель const (this) указателю (const), отличному от const. –

+3

@drspod: Нет такой конструкции, как конструктор const. это никогда не будет const в любом конструкторе, если бы он был const, как бы вы инициализировали члены? – dalle

ответ

3

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

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

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

+0

Спасибо. Эти цитаты из стандарта были теми, которые я искал. Поэтому, если бы я пометил 'foo :: i' как изменчивый, тогда было бы хорошо. – dalle

+0

Поведение будет определено тогда, но вам лучше определить 'foo instance (0);' и 'foo const & f = instance;'. – avakar

1

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

mutable int i;

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


Например:

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

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

+2

'mutable' должен использоваться для членов данных, которые могут быть изменены, но которые не нарушают« константу »ссылки. Это относится, например, к временным переменным. Нет никаких признаков того, что это имеет место в случае OP. – shoosh

+0

Нет никаких указаний на то, что это тоже не так. Я объяснил (надеюсь) ясно, когда он должен (не) использовать его; если он захочет оскорбить его, то это слишком плохо для него. Я согласен с тем, что я не должен был говорить «Это один из случаев, когда ...», я изменю его на «Это может быть одно». –

+0

'mutable' не требуется, поскольку у меня уже есть указатель non-const для объекта. Пересмотр вопроса. – dalle

0

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

Любая причина для создания объекта как const, хотя его состояние не является постоянным?

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

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

{ instance = this; } 
+0

'const_cast' не требуется, так как у меня уже есть указатель не const const. – dalle

0

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

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

+0

Он присваивает 'instance' в конструкторе. – avakar

0

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

Однако, это прекрасно, если у вас есть два указателя на одно значение, одно const, а другое нет.

+0

Извините, что отсутствует int. Извините за добавленную путаницу. – dalle

1

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

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

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

2

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

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

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

+1

Сделайте это. Храните неконтекстные указатели в реестре и поддерживайте функции const и non-const. – Alan

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