Простой способ понять это - представить объект, для которого вызывается метод, как 0-й параметр:
например: Подумайте о
String& MyClass::doSomething();
MyClass mc;
mc.doSomething();
как причудливый, поддерживаемый компилятором способ сделать это:
String& MyClass_doSomething(MyClass *this);
MyClass mc;
MyClass_doSomething(&mc);
Теперь перегрузка, основанная на константности, является лишь частным случаем перегрузки по типам параметров:
String& MyClass::doSomething();
String& MyClass::doSomething() const;
может мыслиться примерно так:
String& MyClass_doSomething(MyClass *this);
String& MyClass_doSomething(const MyClass *this);
Что касается полезности этого, простейшими примерами являются методы begin
и end
в различных контейнерах std::
. Если vector
/ string
/ что-либо не является константным, и вы звоните begin()
, вы получите обратно iterator
, который можно использовать для изменения содержимого исходного контейнера. Очевидно, что это не должно быть разрешено для контейнеров, помеченных const
, поэтому, благодаря перегрузке const / non const, вызов begin()
для вектора const возвращает const_iterator
, который может использоваться для чтения содержимого вектора , но не изменять его.
, например
string nc = "Hello world";
for (string::iterator iString = nc.begin(); iString != nc.end(); ++iString)
{
cout << *iString;; // legal to read
*iString = toupper(*iString); // legal to write
}
const string c = "Hello world again";
for (string::const_iterator iString = c.begin(); iString != c.end(); ++iString)
{
cout << *iString;; // legal to read
// *iString = toupper(*iString); // Writing is illegal
}
Что касается перегрузки по типу возвращаемого значения, вы не можете сделать это в C ++. Тем не менее, вы можете моделировать его довольно прилично.