n–>prev = this–>prev; // предшественник этого объекта
// становится
// предшественником объекта n
this–>prev = n; // n становится предшественником этого
// объекта
return n;
}
Это объяснение выглядит немного многословным, но мы не обязаны упоминать, что указатель
this
обеспечивает доступ к члену класса, поэтому код можно сократить.
Link* Link::insert(Link* n) // вставляет n перед p; возвращает n
{
if (n==0) return this;
if (this==0) return n;
n–>succ = this; // этот объект следует за n
if (prev) prev–>succ = n;
n–>prev = prev; // предшественник этого объекта
// становится
// предшественником объекта n
prev = n; // n становится предшественником этого
// объекта
return n;
}
Иначе говоря, при каждом обращении к члену класса происходит неявное обращение к указателю
this
. Единственная ситуация, в которой его необходимо упомянуть явно, возникает, когда нужно сослаться на весь объект.
Обратите внимание на то, что указатель
this
имеет специфический смысл: он ссылается на объект, для которого вызывается функция-член. Он не указывает на какой-то из ранее использованных объектов. Компилятор гарантирует, что мы не сможем изменить значение указателя
this
в функции-члене. Рассмотрим пример.
struct S {
// ...
void mutate(S* p)
{
this = p; // ошибка: указатель this не допускает изменений
// ...
}
};
17.10.1. Еще раз об использовании списков
Сталкиваясь с вопросами реализации, мы можем увидеть, как выглядит использование списка.
Link* norse_gods = new Link("Thor");
norse_gods = norse_gods–>insert(new Link("Odin"));
norse_gods = norse_gods–>insert(new Link("Zeus"));
norse_gods = norse_gods–>insert(new Link("Freia"));
Link* greek_gods = new Link("Hera");
greek_gods = greek_gods–>insert(new Link("Athena"));
greek_gods = greek_gods–>insert(new Link("Mars"));
greek_gods = greek_gods–>insert(new Link("Poseidon"));
Это очень похоже на предыдущие фрагменты нашей программы. Как и раньше, исправим наши ошибки. Например, укажем правильное имя бога войны.
Link* p = greek_gods–>find("Mars");
if (p) p–>value = "Ares";
Перенесем Зевса в список греческих богов.
Link* p2 = norse_gods–>find("Zeus");
if (p2) {
if (p2==norse_gods) norse_gods = p2–>next();
p2–>erase();
greek_gods = greek_gods–>insert(p2);
}
И наконец, выведем список на печать.
void print_all(Link* p)
{
cout << "{ ";
while (p) {
cout << p–>value;
if (p=p–>next()) cout << ", ";
}
cout << " }";
}
print_all(norse_gods);
cout<<"\n";
print_all(greek_gods);
cout<<"\n";
В итоге получим следующий результат:
{ Freia, Odin, Thor }
{ Zeus, Poseidon, Ares, Athena, Hera }
Какая из этих версий лучше: та, в которой функция
insert()
и другие являются функциями-членами, или та, в которой они не принадлежат классу? В данном случае это не имеет значения, но вспомните, что было написано в разделе 9.7.5.
Следует отметить, что мы создали не класс списка, а только класс узла. В результате мы вынуждены следить за тем, какой указатель ссылается на первый элемент. Эти операции можно было бы сделать лучше, определив класс
List
, но структура класса, продемонстрированная выше, является общепринятой. Стандартный класс
list
рассматривается в разделе 20.4.
Задание
Это задание состоит из двух частей. Первые упражнения должны дать вам представление о динамических массивах и их отличии от класса
vector
.
1. Разместите в свободной памяти массив, состоящий из десяти чисел типа
int
, используя оператор
new
.