Может ли stitic функция-член быть const?

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Ответить
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

И как это декларировать? Есть класс, пусть его зовут A. Есть static функция-член этого класса, знающего все свои экземпляры. Функцию зовут find, она возвращает

Код: Выделить всё

const A*
и должна только найти экземпляр и вернуть указатель на него, но не может менять значения каких либо членов класса. Как это правильно декларировать?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Ну это вопрос уровня джуниора. Если честно, я даже не ожидал от тебя такого.

Если вкратце, то никак. Статический метод не может быть константным!

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

Начнём с того, что любой нестатический метод класса имеет первый неявный параметр this. Это указатель на объект, с которым будет работать метод. Если класс называется A, то this будет иметь тип A* const, что обозначает константный указатель на неконстантные данные. Внутри метода при любом обращении к нестатическому полю класса компилятор будет неявно подставлять перед полем this->, и за счёт того, что наш this указывает на неконстантный объект, программист сможет как читать, так и модифицировать это поле.

Дальше больше. Перед разработчиками стандарта стал вполне очевидный вопрос, напрямую связанный с одним из столпов ООП, а именно инкапсуляцией. Вопрос в том, как можно запретить модификацию данных внутри метода? Очевидным решением было бы сделать наш this указателем на константные данные, после чего компилятор нам сам запретит любые модификации. Но как это задекларировать, если наш this является скрытым параметром и никак не фигурирует в прототипе метода? Решение было найдено. Квалификтор const разместили в конце метода после закрывающейся скобки. Такое объявление метода следует понимать как то, что const на самом деле относится к this, в следствие чего последний меняет свой тип на const A* const, то есть константный указатель на константные данные.

Ну и теперь перейдём наконец-то к статическим методам. Любой даже начинающий программист С++ скажется, что статический метод - это метод, который может работать только со статическими полями класса, а ещё он может быть вызван без объекта класса. Если копнуть чуть глубже, то можно понять в чём же тут дело на самом деле. Всё очень просто. Статический метод не имеет скрытого параметра this. Остальные его свойства - это следствие. Он не может менять нестатические поля потому, что доступ к нестатическим полям осуществляется через this, которого он не имеет. И он может быть вызван без объекта, так как объект (который и является this'ом), для его вызова просто не нужен. Теперь вопрос. Если мы напишем при объявлении такого метода в конце const, то куда этот const на самом деле подставится? Скрытого параметра ведь нет. Да никуда не подставится! Именно поэтому статический метод не может быть константным. И именно поэтому программисту придётся самому следить за тем, чтобы статический метод не менял статические поля класса, если ему требуется подобное поведение, так как на уровне компилятора запретить изменение просто невозможно.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Romeo писал(а):Ну и теперь перейдём наконец-то к статическим методам. Любой даже начинающий программист С++ скажется, что статический метод - это метод, который может работать только со статическими полями класса, а ещё он может быть вызван без объекта класса.
Вообще то нет. Если класс знает хотябы один свой экземпляр, то его знает и любой метод. Соответственно любой метод может работать с любыми полями этого экземпляра. В данном случае класс знает все экземпляры и его методы могут работать с полями всех экземпляров.

Код: Выделить всё

class A
{
 private:
  static A *First;
  static A *Last;
  A *Previous;
  A *Next;
 ...
};

Код: Выделить всё

const
A*
A::find (const std::wstring &s)
{
 A *p;
 for (p=First; p!=nullptr; p=p->Next)
 {
  if (p->s==s)
  {
   return (const A*)p;
  }
 }
 return nullptr;
}
.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Romeo писал(а):И именно поэтому программисту придётся самому следить за тем, чтобы статический метод не менял статические поля класса, если ему требуется подобное поведение, так как на уровне компилятора запретить изменение просто невозможно.
А чего за этим следить? Ничего лишнего в любом случае не меняется. Но представьте себе: объект сконструирован временно из литерала, или из экземпляра другого класса, потом передан методу по константной ссылке. Передавать по обычной ссылке нельзя, так как тогда окажется запрещена передача временных объектов. Передавать по значению может быть тоже нельзя из-за проблем с копированием полей и даже если таковых нет, всё равно нежелательно, так как это ведёт к копированию объекта и созданию лишнего объекта в памяти даже в том случае, если объект, для которого вызывается метод, не временный. Поэтому константная ссылка. Но этот методы вызывает для того же объекта ещё один метод. Вызвать не константный метод нельзя, так как объект - константа. А тот методы вызывает статический метод. Вызвать не константный метод опять нельзя, так вызывающий - константный. И не статическим этот третий метод тоже быть не может, так как может быть вызван откуда угодно и при этом вызов должен не зависеть от конкретных имён и адресов каких либо объектов. Более того, этот третий метод должен работать даже в том случае, если класс не имеет ни одного экземпляра в той области видимости, в которой вызывается. И даже в том случае, когда класс ещё не имеет вообще ни одного экземпляра.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

А почему ты пишешь "вообще-то нет". Как мои слова противоречат твоим? Ты точно понял моё предложение, которое сам же выделил в своём сообщении? :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Ну и раз уж пошла такая пьянка, то для того, что ты делаешь, обычно используют кеширующую фабрику, а не статические методы и поля внутри самого класса. О плюсах фабрики распространяться не буду, так как это отдельная и весьма объёмная тема. Да и лучше будет GOF'а почитать, чем меня :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

А вот это:
Romeo писал(а):Ты точно понял, что моё предложение, которое сам же выделил в своём сообщении? :)
я уже не распарсил.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Ответить