Функции-элементы типа const
Рассмотрим следующие фрагменты программного кода:
const Stock land=Stock("Kludgehorn Properties");
land.show();
В рассматриваемой версии C++ компилятор отвергает вторую строку. Почему? Да потому, что код функции show() не дает гарантии, что не будет модифицирован вызывающий объект, который, будучи объявленным как const, не должен подвергаться изменениям. Все, что необходимо в таких случаях, — это новое синтаксическое средство, которое гарантирует, что функция не внесет изменений в вызывающий ее объект. В C++ эта проблема решается с помощью ключевого слова const, которое ставится после скобок функции. Другими словами, объявление show() принимает такой вид:
void show()const; //обещает не вносить изменений в вызывающий объект
Аналогично начальная часть определения функции принимает такой вид:
void stock::show()const // обещает не вносить изменений в вызывающий объект
Функции, объявленные и определенные таким способом, называются функциями-элементами типа const. Необходимо размещать методы класса в категории const всякий раз, когда нужно, чтобы они не изменяли вызывающий объект. Мы будем впредь пользоваться этим правилом.
|
|
Работа с указателем this
Продолжим работу с классом Stock. До сих пор каждая функция-элемент этого класса имела дело с одним-единственным объектом. Этим объектом был объект, который обращался к ней. Однако иногда возникает потребность в методе, который работает с двумя объектами, и эта задача решается с использованием специального указателя C++, получившего имя this.
Несмотря на то, что объявление класса Stock отображает данные, ему не хватает аналитических возможностей. Например, проанализировав выходные данные функции show(), вы сможете сказать, какой из ваших вкладов превосходит по величине все остальные. Однако программа этого сделать не может, поскольку у нее нет непосредственного доступа к значению total_val.
Воспользуемся подходом, позволяющим изучить указатель this. Этот подход состоит в том, что определяется функция-элемент, которая просматривает два объекта Stock и возвращает ссылку на тот из них, который больше по величине. При попытке реализовать этот подход возникает ряд вопросов.
Во-первых, как будет выглядеть функция-элемент, выполняющая сравнение двух объектов? Предположим, например, что вы решили назвать такой метод topval(). Далее, при вызове функции stockl.topvaI() обеспечивается доступ к данным объекта stock1, в то время как сообщение stock2.topval() получает доступ к данным объекта stock2. Если вы хотите, чтобы указанный выше метод провел сравнение двух объектов, то необходимо передать второй объект как аргумент. В целях повышения производительности передайте этот аргумент по ссылке. Иначе говоря, пусть метод topval() использует аргумент типа const Stock&.
|
|
Во-вторых, как вы возвратите ответ этого метода в вызывающую программу? Наиболее простой способ: заставить метод возвратить ссылку на объект, который имеет наибольшую общую стоимость. Таким образом, метод, выполняющий сравнение, должен иметь следующий прототип:
const Stock & topval(const Stock & s) const;
Эта функция осуществляет неявный доступ к одному из объектов, к другому объекту она получает прямой доступ, при этом она возвращает ссылку на один из двух объектов. Ключевое слово const в круглых скобках означает, что функция не вносит изменений в объект, к которому она получает прямой доступ, а ключевое слово const, которое следует сразу за скобками, означает, что функция не подвергает модификации объект, к которому она осуществляет неявный доступ. Поскольку функция возвращает ссылку на один из объектов const, возвращаемый тип также имеет ссылку const.
Предположим далее, что вы хотите сравнить объекты stockl и stock2 класса Stock и присвоить тот из них, который имеет большую общую стоимость, объекту top. Для этого можно воспользоваться любым из следующих двух операторов:
top = stockl.topval(stock2);
top = stock2.topval(stockl);
Первый из них получает прямой доступ к объекту stockl и неявный доступ к объекту stock2, в то время как второй получает прямой доступ к объекту stockl и неявный доступ к объекту stock2.
Каким бы ни был доступ, этот метод сравнивает два объекта и возвращает ссылку на тот из них, у которого общая стоимость больше.
Между тем, мы еще не все выяснили из того, как реализовать функцию topval(). При этом возникает небольшая проблема. Ниже представлена частичная реализация, которая позволяет нам вникнуть в суть этой проблемы.
const Stock & Stock::topval(const Stock & s) const
{
if (s.total_val > total_yal)
return s; //аргумент типа объект
else
return ?????; //объект, вызывающий метод topval
}
Здесь s.total_val — это общее значение для объекта, переданного в качестве аргумента, a total_val — общее значение для объекта, которому было отправлено сообщение. Если значение s.total_val больше значения total_val, то функция возвращает s. В противном случае она возвращает объект, использованный для вызова метода. (В среде ООП говорят, что это объект, которому передается сообщение topval.) Проблема заключается в том, как назвать этот объект? Если вы присвоите ему имя stockl.topval(stock2), то s будет ссылкой на stock2 (т.е. псевдоним для stock2), однако псевдонима у stockl нет.
В C++ эта проблема решается путем использования специального указателя this. Указатель this ссылается на объект, который используется для вызова функции-элемента. (По существу, указатель this передается как скрытый аргумент рассматриваемого метода.) Следовательно, при вызове функции stockl.topval(stock2) устанавливается указатель this на адрес объекта stockl и обеспечивается доступ к этому указателю со стороны метода topval(). Аналогично при вызове функции stock2.topval(stockl) устанавливается указатель this на адрес объекта stock2. В общем случае для всех методов класса указатель this указывает на адрес объекта, который вызывает этот метод. И в самом деле, total_val в методе topval() — всего лишь сокращенное обозначение от this->totaI_val. (Напомним, что вы пользовались оператором -> для доступа к элементам структур посредством указателя. То же самое справедливо и для элементов класса.)
УКАЗАТЕЛЬ THIS
Каждая функция-элемент, включая конструкторы и деструкторы, имеет указатель this. Характерной особенностью указателя this является то, что он указывает на вызывающий объект. Если у какого-либо метода возникает необходимость ссылки на объект как на единое целое, он может воспользоваться выражением *this. Спецификатор const, помещенный сразу за скобками, в которые заключены аргументы, истолковывает в рассматриваемом случае указатель this как const; в такой ситуации вы не можете использовать this, чтобы изменить значение объекта.
|
|
Однако то, что вы хотите возвратить, — это вовсе не указатель this, так как this выражает адрес объекта. Вы же хотите вернуть сам объект, а он обозначается как *this. (Напомним, что в результате применения оператора разыменования * к указателю получается величина, на которую указывает указатель.) Теперь вы можете завершить определение метода, используя *this как псевдоним вызывающего объекта.
const Stock & Stock::topval(const Stock & s) const
{
if (s.total_val > total_val)
return s; //объект в качестве аргумента else
return *this; //вызывающий объект
}
Тот факт, что возвращаемый тип является ссылкой, означает, что возвращаемый объект сам является вызывающим объектом, и ни в коем случае не означает, что механизм возврата передает копию.
Задание
Напишите пояснение к данной функции:
const Stock & Stock::topval(const Stock & s) const
{
if (s.total_val > total_val)
return s; //объект в качестве аргумента else
return *this; //вызывающий объект
}