- •11. Инкапсуляция
- •12. Полиморфизм
- •13. Наследование
- •Управление доступом
- •Элементы класса
- •Элементы данных
- •Элементы функции
- •15. Перегрузка функций
- •16. Конструкторы и деструкторы Конструктор
- •Деструктор
- •17. Параметризированные конструкторы
- •18. Дружественные функции
- •19. Inline-функции
- •20. Передача объектов в функции
- •21. Возвращение объектов функциями
- •22. Массивы объектов
- •23. Указатели на объекты
- •24. Указатель this
- •25. Перегрузка операторов
- •26. Ссылки
- •27. Наследование и спецификаторы доступа
- •28. Конструкторы и деструкторы производных классов
- •29. Множественное наследование. Передача параметров в конструктор базового класса
- •30. Указатели и ссылки на производные типы
- •31. Виртуальные функции
- •32. Чисто виртуальные функции и абстрактные типы
- •33. Раннее и позднее связывание
- •35. Создание собственных операторов вставки и извлечения.
- •36. Форматирование ввода-вывода
- •37. Файловый ввод-вывод
- •38. Чтение и запись в текстовые файлы
- •39. Двоичные файлы
- •40. Функции-шаблоны
- •41. Классы-шаблоны
- •42. Использование пространства имён
- •43. Контейнерные классы
- •46. Основы обработки исключений
- •47. Идентификация типа во время исполнения (rtti)
- •49. Виртуальный базовый класс
22. Массивы объектов
Можно создавать массивы объектов точно так же, как создаются массивы данных других типов.
Инициализация массивов объектов
Если класс определяет конструктор с параметрами, то можно инициализировать каждый объект массива путем указания списка инициализации в точности так же, как это делается для массивов других типов. Однако точная форма списка инициализации будет определяться числом параметров конструктора.
Пример
#include <iostream>
using namespace std;
class cl{
int h;
int i;
public:
cl(int j, int k) {h=j; i=k;}
int get_i() {return i; }
int get_h() {return h;}
};
int main()
{
cl ob[3] = {
cl(1, 2),
cl(3, 4),
cl(5, 6)
};
int i;
for (i=0; i<3; i++) {
cout << ob[i].get_h();
cout << “, “;
cout << ob[i].get_i() << “\n”;
}
return 0;
}
Для создания неициализированных массивов необходимо иметь конструктор без параметров.
23. Указатели на объекты
В С++ можно получить доступ к объекту как непосредственно, так и используя указатель на объект.
Пример
#include <iostream>
using namespace std;
class P_example {
int num;
public:
void set_num(int val){num=val;}
void show_num();
};
void P_example::show_num()
{
cout << num << “\n”;
}
int main()
{
P_example ob, *p;
ob.set_num(1);
ob.show_num();
p=&ob;
p->show_num();
return 0;
}
24. Указатель this
Каждая нестатическая функция-элемент имеет доступ к объекту, для которого вызвана, через ключевое слово this. Типом this является:
тип_класса *.
Пример.
class simple
{
public:
simple();
void greet() { cout<<“ Hello!”;};
};
simple::simple()
{
greet(); // вызов
this>greet(); // функции
(*this).greet(); // greet()
}
Т.к. функции-элементы могут обращаться ко всем элементам класса просто по имени, в основном указатель this используется для возврата указателя (return this) или ссылки (return *this) на подразумеваемый объект.
25. Перегрузка операторов
С++ поддерживает перегрузку операторов. За небольшими исключениями большинство операторов С++ могут быть перегружены, в результате чего они получат специальное значение по отношению к определённым классам. Например, класс, определяющий связанный список, может использовать оператор + для того, чтобы добавлять объект к списку. Другой класс может использовать оператор + совершенно иным способом. Когда оператор перегружен, ни одно из его исходных значений не теряет смысла. Просто для определённого класса объектов определён новый оператор. Поэтому перегрузка оператора + для того чтобы обрабатывать связный список, не изменяет его действия по отношению к целым числам.
Операторные функции обычно будут или членами, или друзьями того класса, для которого они используются. Несмотря на большое сходство, имеется определённое различие между способами, которыми перегружаются операторные функции-члены и операторные функции-друзья.
Для того, чтобы перегрузить оператор, необходимо определить, что именно означает оператор по отношению к тому классу, к которому он применяется. Общая форма записи функции-оператора для случая, когда она является членом классом класса, имеет вид:
тип имя_класса::operator#(список аргументов)
{
// действия, определённые применительно к классу
}
Здесь перегруженный оператор подставляется вместо символа #, а тип задает тип значений, возвращаемых оператором. Для того, чтобы упростить использование перегруженного оператора в сложных выражениях, в качестве возвращаемого значения часто выбирают тот же самый тип, что и класс, для которого перегружается оператор.
Пример
Листинг 1
Создаётся класс three_d, содержащий координаты объекта в трёхмерном пространстве. Следующая программа перегружает операторы + и = для класса three_d.
#include <iostream>
using namespace std;
class three_d {
int x,y,z;
public:
three_d operator+(three_d t);
three_d operator=(three_d t);
void show();
void assign(int mx, int my, int mz);
};
//
three_d three_d::operator+(three_d t)
{
three_d temp;
temp.x=x+t.x;
temp.y=y+t.y;
temp.z=z+t.z;
return temp;
}
//
three_d three_d::operator=(three_d t)
{
x=t.x;
y=t.y;
z=t.z;
return *this;
}
//
void three_d::show()
{
cout << x <<",";
cout << y <<",";
cout << z <<"\n";
}
//
void three_d::assign(int mx, int my, int mz)
{
x=mx;
y=my;
z=mz;
}
int main()
{
three_d a,b,c;
a.assign(1,2,3);
b.assign(10,10,10);
a.show();
b.show();
c=a+b;
c.show();
c=a+b+c;
c.show();
c=b=a;
c.show();
b.show();
return 0;
}
Если рассмотреть эту программу внимательно, может вызвать удивление, что обе функции-оператора имеют только по одному параметру, несмотря на то, что они перегружают бинарный оператор. Это связано с тем, что при перегрузке бинарного оператора с использованием функции-члена ей передаётся явным образом только один аргумент. Вторым аргументом служит указатель this, который передаётся ей неявно. Так, в строке
temp.x = x+t.x;
x соответствует this->x, где x ассоциировано с объектом, который вызывает функцию-оператор. Объект, стоящий справа от знака операции, передаётся функции.
При перегрузке унарной операции функция-оператор не имеет параметров, а при перегрузке бинарной операции функция-оператор имеет один параметр. (Нельзя перегрузить триадный оператор.) Во всех случаях объект, активизирующий функцию-оператор, передаётся неявным образом с помощью указателя this.
Тщательно проанализируем, как работает программа из листинга 1, начиная с перегруженного оператора +. Когда два объекта типа three_d подвергаются воздействию оператора +, значения их соответствующих координат складываются, как показано в функции operator+(), ассоциированной с данным классом. Обратим внимание на то, что функция не модифицирует значений операндов. Вместо этого она возвращает объект типа three_d, содержащий результат выполнения операции.
Другим ключевым моментом перегрузки оператора сложения является то, что он возвращает объект типа three_d. Хотя функция может иметь в качестве значения любой допустимый тип языка С++, то, что она возвращает объект типа three_d, позволяет использовать оператор + в более сложных выражениях, таких как a+b+c. Здесь a+b создаёт результат типа three_d. Это значение затем прибавляется к c.
В противоположность оператору +, оператор присваивания модифицирует свои аргументы. Поскольку функция operator=() вызывается объектом, стоящим слева от знака равенства, то именно этот объект модифицируется. Однако даже оператор присваивания обязан возвращать значение, поскольку оператор присваивания порождает величину, стоящую с правой стороны равенства. Так, для того чтобы выражение следующего вида
a = b = c = d;
было допустимым, необходимо, чтобы оператор operator=() возвращал объект, на который указывает this и который будет объектом, стоящим с левой стороны. Если сделать таким образом, то можно выполнить множественное присваивание.
Следующие операторы не могут быть перегружены:
. :: .* ?