Что дальше
Последняя неделя начинается с изучения дополнительных возможностей наследования. Затем на занятии 16 вы изучите потоки, а на занятии 17 познакомитесь с одним замечательным дополнением стандартов C++ — пространствами имен. Занятие 18 посвящено анализу основ объектно-ориентированного программирования. В этот день внимание будет сконцентрировано не столько на синтаксисе языка, сколько на изучении концепций объектно-ориентированного программирования. На занятии 19 вы познакомитесь с использованием шаблонов, а на занятии 20 узнаете о методах отслеживания исключительных ситуаций и ошибок. Наконец, на последнем занятии будут раскрыты некоторые хитрости и секреты программирования на C++, что сделает вас настоящим гуру в этой области.
День 15-й. Дополнительные возможности наследования
До настоящего момента вы использовали одиночное и множественное наследование для создания относительно простых связей между классами. Сегодня вы узнаете:
• Что такое вложение и как его использовать
• Что такое делегирование и как его использовать
• Как выполнить один класс внутри другого
• Как использовать закрытое наследование
Вложение
Анализируя примеры, приведенные на предыдущих занятиях, вы, вероятно, заметили, что в классах допускается использование в переменных-членах объектов других классов. В этом случае программисты на C++ говорят, что внешний класс содержит внутренний. Так, класс Employee в качестве переменных-членов может содержать строковые объекты (с именем сотрудника) и объекты с целочисленными значениями (зарплатой и т.д.).
В листинге 15.1 представлен незавершенный, но весьма полезный класс String. Запуск такой программы не приведет к выводу каких-либо результатов, но она потребуется при написании других программ этого занятия.
Листинг 15.1. Класс string
1: #include <iostream.h>
2: #include <string.h>
3:
4: class String
5: {
6: public:
7: // конструкторы
8: String();
9: String(const char *const);
10: String(const String &)
11: ~String();
12:
13: // перегруженные операторы
14: char & operator[](int offset);
15: char operator[](int offset) const;
16: String operator+(const String&);
17: void operator+=(const String&);
18: String & operator= (const String &);
19:
20: // Общие методы доступа
21: int GetLen()const { return itsLen; }
22: const char * GetString() const { return itsString; }
23: // статический целочисленный счетчик ConstructorCount;
24:
25: private:
26: String (int); // закрытый конструктор
27: char * itsString;
28: unsigned short itsLen;
29:
30: };
31:
32: // конструктор класса String пo умолчанию создает строку длиной 0 байт
33: String::String()
34: {
35: itsString = new char[1];
36: itsString[0] = ' ';
37: itsLen=0;
38: // cout << "tDefault string constructorn";
39: // ConstructorCount++;
40: }
41:
42: // закрытый конструктор, используемый только
43: // методами клаcса для создания новой cтроки
44: // указанного размера, заполненной нулями
45: String::String(int len)
46: {
47: itsString = new ohar[len+1];
48: for (int i = 0; i<=len; i++)
49: itsString[i] = ' ';
50: itsLen=len;
51: // cout << "tString(int) constructorn";
52: // ConstructorCount++;
53: }
54:
55: // Преобразует массив символов в cтроку
56: String::String(const char * oonst cString)
57: {
58: itsLen = strlen(cString);
59: itsString = new char[itsLen+1];
60: for (int i = 0; i<itsLen; i++)
61: itsString[i] = cString[i];
62: itsString[itsLen]=' ';
63: // cout << "tString(char*) constructorn";
64: // ConstructorCount++;
65: }
66:
67: // конструктор-копировщик
68: String::String (const String & rhs)
69: {
70: itsLen=rhs.GetLen();
71: itsString = new char[itsLen+1];
72: for (int i = 0; i<itsLen;i++)
73: itsString[i] = rhs[i];
74: itsString[itsLen] = ' ';
75: // cout << "tString(String&) constructorn
76: // ConstructorCount++;
77: }
78:
79: // деструктор освобождает занятую память
80: String::~String ()
81: {
82: delete [] itsString;
83: itsLen = 0;
84: // cout << "tString destructorn";
85: }
86:
87: // этот оператор освобождает память, а затем
88: // копирует строку и размер
89: String& String::operator=(const String & rhs)
90: {
91: if (this == &rhs)
92: return *this;
93: delete [] itsString;
94: itsLen=rhs.GetLen();
95: itsString = new char[itsLen+1];
96: for (int i = 0; i<itsLen;i++)
97: itsString[i] = rhs[i];
98: itsString[itsLen] = ' ';
99: return *this;
100: // cout << "tString operator=n";
101: }
102:
103: // неконстантный оператор индексирования,
104: // возвращает ссылку на символ, который можно
105: // изменить
106: char & String::operator[](int offset)
107: {
108: if (offset > itsLen)
109: return itsString[itsLen-1];
110: else
111: return itsStnng[offset];
112: }
113:
114: // константный оператор индексирования,
115: // используется для константных объектов (см. конструктор-копировщик!)
116: char String::operator[](int offset) const
117: {
118: if (offset > itsLen)
119: return itsString[itsLen-1];
120: else
121: return itsString[offset];
122: }
123:
124: // создает новую строку, добавляя текущую
125: // строку к rhs
126: String String::operator+(const String& rhs)
127: {
128: int totalLen = itsLen + rhs.GetLen();
129: String temp(totalLen);
130: int i, j;
131: for (i = 0; i<itsLen; i++)
132: temp[i] = itsString[i];
133: for (j = 0: j<rhs.GetLen(); j++, i++)
134: temp[i] = rhs[j];
135: temp[totalLen]=' ';
136: return temp;
137: }
138:
139: // изменяет текущую строку, ничего не возвращая
140: void String::operator+=(const String& rhs)
141: {
142: unsigned short rhsLen = rhs.GetLen();
143: unsigned short totalLen = itsLen + rhsLen;
144: String temp(totalLen);
145: int i, j;
146: for (i = 0; i<itsLen; i++)
147: temp[i] = itsString[i];
148: for (j = 0; j<rhs.GetLen(); j++, i++)
149: temp[i] = rhs[i-itsLen];
150: temp[totalLen]=' ';
151: *this = temp;
152: }
153:
154: // int String::ConstructorCount = 0;
Результат:
Нет
Анализ: Представленный в листинге 15.1 класс String напоминает другой класс, использованный в листинге 12.12. Однако есть одно важное отличие между этими двумя классами: конструкторы и некоторые функции листинга 12.12 включали операторы вывода на печать, благодаря которым на экране отображались сообщения об их использовании. В листинге 15.1 эти операторы временно заблокированы, но они будут использоваться в следующих примерах.
Статическая переменная-член ConstructorCount объявляется и инициализируется соответственно в строках 23 и 154. Значение этой переменной увеличивается на единицу при вызове любого конструктора класса String. Эти функции также заблокированы и будут использоваться в следующих листингах.
В листинге 15.2 объявляется класс Employee, содержащий три объекта класса String.
Листинг 15.2. Класс Employee
1: #include "String.hpp"
2:
3: class Employee
4: {
5:
6: public:
7: Employee();
8: Employee(char *, char *, char >>, long);
9: ~Employee();
10: Employee(const Employee&);
11: Employee & operator= (const Employee &);
12:
13: const String & GetFirstName() const
14: { return itsFirstName; }
15: const String & GetLastName() const { return itsLastName; }
16: const String & GetAddress() const { return itsAddress; }
17: long GetSalary() const { return itsSalary; } 18;
19: void SetFirstName(const String & fNama)
20: { itsFirstName = fName; }
21: void SetLastName(const String & lNama)
22: { itsLastName = lNamo; }
23: void SetAddress(const String & address)
24: { itsAddress = address; }
25: void SetSalary(long salary) { itsSalary = salary; }
26: private:
27: String itsFirstName;
28: String itsLastName;
29: String itsAddress;
30: long itsSalary;
31: };
32:
33: Employee::Employee();
34: itsFirstName(""),
35: itsLastName(""),
36: itsAddress(""),
37: itsSalary(0)
38: { }
39:
40: Employee::Employee(char * firstName, char * lastName,
41: char * address, long salary):
42: itsFirstName(firstName),
43: itsLastName(lastName),
44: itsAddress(address),
45: itsSalary(salary)
46: { }
47:
48: Employee::Employee(const Employee & rhs):
49: itsFirstName(rhs.GetFirstName()),
50: itsLastName(rhs,GetLastName()),
51: itsAddress(rhs,GetAddress()),
52: itsSalary(rhs.GetSalary())
53: { }
54:
55: Employee::~Employea() { }
56:
57: Employee & Employae::Qperator= (const Employee & rhs)
58: {
59: if (thls — &rhs)
60: return *this;
61:
62: itsFlrstName = rhs.GetFlrstName();
63: itsLastName = rhs,GetLastName();
64: itsAddress = rhs,GetAddress();
65: itsSalary = rhs,GetSalary();
66:
67: return *thls;
68: }
69:
70: int main()
71: {
72: Employee Edie("Jane","Doe","1461 Shore Parkway", 20000);
73: Edie.SetSalary(50000);
74: String LastName("Levine");
75: Edie.SetLastName(LastName);
76: Edie.SetFirstName("Edythe");
77:
78: cout << "Имя: ";