Михаил Ильин ([info]yorool_gui) wrote,
@ 2008-07-05 22:46:00
Previous Entry  Add to memories!  Tell a Friend!  Next Entry
обять об const
С подачи [info]eldog опять по поводу вот этого мудацкого const. Мудацкость тут в том, что const как бы говорит нам, что метод ничего не меняет внутри класса. Но мы-то знаем, что метод этот все же что-то меняет. Значит мы можем предполагать, что меняет он что-то снаружи и уже опасаемся даже вызывать его у разных объектов или из разных тредов. Хотя на самом деле все не так плохо.


(Post a new comment)

> вот этого мудацкого const.
[info]poige
2008-07-06 04:37 am UTC (link)
По-моему, ты слишком глубоко отнёсся к этому const. Насколько я понимаю, const прост — гарантируется, что данные объекта класса не меняются вызовом метода. И это всё, что гарантируется.

(Reply to this)(Thread)

Re: > вот этого мудацкого const.
[info]rumataestor
2008-07-06 08:00 am UTC (link)
Ничего не гарантируется. mutable можно менять сколько хочешь.

(Reply to this)(Parent)(Thread)

Re: > вот этого мудацкого const.
[info]some41
2008-07-06 10:14 am UTC (link)
на самом деле _вообще_ ничего не гарантируется -- можно делать const_cast сколько хочешь.

(Reply to this)(Parent)(Thread)

Re: > вот этого мудацкого const.
[info]kodt_rsdn
2008-07-07 09:44 am UTC (link)
Отнюдь! const_cast ты не можешь делать сколько хочешь, потому что объект может быть истинной константой, и попытка его модифицировать приведёт к неопределённому поведению.

Другое дело, что можно получить честный доступ к неконстантному самому себе:

class Foo
{
  Foo* myself;
public:
  void set_myself() { myself = this; }

  Foo() { set_myself(); }
  // Нетривиальный конструктор мгновенно запрещает размещать глобальную константу в секции констант.
  // Поэтому реализацию UB через AV мы не получим никогда; только сломанную логику.
  // Впрочем, пофиг. Мы можем пока что не сосредотачиваться на истинных константах.

  // А вот здесь мы сделаем феерверк
  void bang();
  void hack() const { myself->bang(); }
};


Здесь неконстантый myself уверяет нас, что объект по-настоящему неконстантен, а константность в месте вызова hack() - это лишь накладка поверх.

Точно так же можем поймать леща, например, вот здесь

class Foo
{
public:
  Foo& operator=(Foo const& src)
  {
    ..... // что-то меняем у себя
    ..... // что-то копируем из источника
    ..... // и так сэм раз!
  }
};

Foo x;
x = x;


Это известная грабля, причём свойственная не только С++. С равным успехом (только синтаксически иначе) её можно сделать на любом языке. Только С++ иногда надаёт по пальцам, а тот может не надавать.

{ паскаль }
type Foo = object
    .....
    procedure Assign(const src : Foo);
    .....
  end;

/* cи */
void* memcpy(void* dst, void const* src, size_t num_bytes);


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

Foo& Foo::operator=(Foo const& src)
{
  if(&src==this) return *this; // короткий путь

  // длинный путь (особенно удобен при разделяемом владении)
  Foo temp(src); swap(*this,temp);
}


Вот.
Но более лепо было бы перенести дискуссии на профессиональную площадку - сиречь, RSDN.


P.S. А есть в инете сервис по раскраске кода?

(Reply to this)(Parent)(Thread)

Re: > вот этого мудацкого const.
[info]some41
2008-07-07 08:19 pm UTC (link)
бугага. то есть запретить константные экземпляры чтобы заныкать поинтер можно, а чтобы делать const_cast нельзя? двойные стандарты!

кроме того, никто не запрещает коду выяснить, "по-настоящему" у него константный this или нет. проще всего это сделать проверив this на попадание в диапазон адресов (очевидно, платформозависимый, хотя, например, грубый диапозон адресов стека вычислить тривиально) или запросив у ОС свойства памяти, где мы находимся (аналогично). можно использовать трюки с перегрузкой new. в реализациях, дающих легко обработать ошибки доступа в память (win32, например) можно методом грубой силы попробовать записать поверх *this и посмотреть что будет. ну а после того, как мы выяснили, что объект не константный, можно смело делать каст и писать.


а заалиасить объект на себя можно милионом способов, никакого геройства в этом нет.

да, а оператор присваивания, который для корректности должен проверять this==&src скорее всего не exception safe, что типа не тру.

P.S. в интернете есть всё.

(Reply to this)(Parent)(Thread)

Re: > вот этого мудацкого const.
[info]kodt_rsdn
2008-07-08 10:29 am UTC (link)
Это не двойные стандарты.
Делать можно всё что угодно, но система типов уберегает от некоторых (довольно многочисленных) ошибок доступа. Без константности всё было бы гораздо хуже.

Точно так же, система защиты памяти уберегает от некоторых ошибок доступа. И её также можно обойти - либо подкрутив ручки у линкера (чтобы секцию констант он поместил в RW-страницу), либо принудив компилятор поместить данную константу в секцию данных (инициализация в рантайме, либо mutable член), либо получить административный доступ к менеджеру памяти ОС и изменить тип защиты у страницы.


Любое нарушение константности ведёт к непредсказуемому поведению. Просто потому, что остальные участки программы не ждут такого западла.


const_cast - это ещё один способ заалиасить объект.

А оператор присваивания - не обязательно проблемы с exception safety. Вот, например, известная - я бы даже сказал, академическая грабля.

complex& operator *= (complex& lhs, complex const& rhs)
{
  lhs.re = lhs.re*rhs.re - lhs.im*rhs.im;
  lhs.im = lhs.re*rhs.im + lhs.im*rhs.re;
}


(Если думаешь, что комплексное - это слишком просто, то возьми матрицу - чтобы не возникло желания сразу передать rhs по значению).

(Reply to this)(Parent)


Create an Account
Forgot your login?
Login w/ OpenID
English • Español • Deutsch • Русский…