25: void Part::Display() const
26: {
27: cout << "nPart Number: ";
28: cout << itsPartNumber << endl;
29: }
30:
31: // ************** Класс Car Part ************
32:
33: class CarPart : public Part
34: {
35: public:
36: CarPart():itsModelYear(94){ }
37: CarPart(int year, int partNumber);
38: virtual void Display() const
39: {
40: Part::Display();
41: cout << "Model Year: ";
42: cout << itsModelYear << endl;
43: }
44: private:
45: int itsModelYear;
46: };
47:
48: CarPart::CarPart(int year, int partNumber):
49: itsModelYear(year),
50: Part(partNumber)
51: { }
52:
53:
54: // *********** Класс AirPlane Part ***********
55:
56: class AirPlanePart : public Part
57: {
58: public:
59: AirPlanePart():itsEngineNumber(1){ };
60: AirPlanePart
61: (int EngineNumber, int PartNumber);
62: virtual void Display() const
63: {
64: Part::Display();
65: cout << "Engine No.: ";
66: cout << itsEngineNumber << endl;
67: }
68: private:
69: int itsEngineNumber;
70: };
71:
72: AirPlanePart::AirPlanePart
73: (int EngineNumber, int PartNumber):
74: itsEngineNumber(EngineNumber),
75: Part(PartNumber)
76: { }
77:
78: // **************** Класс Part Node ************
79: class PartNode
80: {
81: public:
82: friend class PartsList;
83: PartNode (Part*);
84: ~PartNode();
85: void SetNext(PartNode * node)
86: { itsNext = node; }
87: PartNode * GetNext() const;
88: Part * GetPart() const;
89: private:
90: Part *itsPart;
91: PartNode * itsNext;
92: };
93:
94:
95: PartNode::PartNode(Part* pPart):
96: itsPart(pPart),
97: itsNext(0)
98: { }
99:
100: PartNode::~PartNode()
101: {
102: delete itsPart;
103: itsPart = 0;
104: delete itsNext;
105: itsNext = 0;
106: }
107:
108: // Возвращается NULL, если нет следующего узла PartNode
109: PartNode * PartNode::GetNext() const
110: {
111: return itsNext;
112: }
113:
114: Part * PartNode::GetPart() const
115: {
116: if (itsPart)
117: return itsPart;
118: else
119: return NULL; //ошибка
120: }
121:
122:
123: // ************** Класс Part List
124: class PartsList
125: {
126: public:
127: PartsList();
128: ~PartsList();
129: // Необходимо, чтобы конструктор-копировщик и оператор соответствовали друг другу
130: void Iterate(void (Part::*f)()const) const;
131: Part* Find(int & position, int PartNumber) const;
132: Part* GetFirst() const;
133: void Insert(Part *);
134: Part* operator[](int) const;
135: int GetCount() const { return itsCount; }
136: static PartsList& GetGlobalPartsList()
137: {
138: return GiobalPartsList;
139: }
140: private:
141: PartNode * pHead;
142: int itsCount;
143: static PartsList GiobalPartsList;
144: };
145:
146: PartsList PartsList::GlobalPartsList;
147:
148: // Implementations for Lists...
149:
150: PartsList::PartsList();
151: pHead(0),
152: itsCount(0)
153: { }
154:
155: PartsList::~PartsList()
156: {
157: delete pHead;
158: }
159:
160: Part* PartsList::GetFirst() const
161: {
162: if (pHead)
163: return pHead->itsPart;
164: else
165: return NULL; // ловушка ошибок
166: }
167:
168: Part * PartsList::operator[](int offSet) const
169: {
170: PartNode* pNode = pHead;
171:
172: if (!pHead)
173: return NULL; // ловушка ошибок
174:
175: if (offSet > itsCount)
176: return NULL; // ошибка
177:
178: for (int i=0;i<offSet; i++)
179: pNode = pNode->itsNext;
180:
181: return pNode->itsPart;
182: }
183:
184: Part* PartsList::Find(int & position, int PartNumber) const
185: {
186: PartNode * pNode = 0;
187: for (pNode = pHead, position = 0;
188: pNode!=NULL;
189: pNode = pNode->itsNext, position++)
190: {
191: if (pNode->itsPart->GetPartNumber() == PartNumber)
192: break;
193: }
194: if (pNode == NULL)
195: return NULL;
196: else
197: return pNode->itsPart;
198: }
199:
200: void PartsList::Iterate(void (Part::*func)()const) const
201: {
202: if (!pHead)
203: return;
204: PartNode* pNode = pHead;
205: do
206: (pNode->itsPart->*func)();
207: while (pNode = pNode->itsNext);
208: }
209:
210: void PartsList::Insert(Part* pPart)
211: {
212: PartNode * pNode = new PartNode(pPart);
213: PartNode * pCurrent = pHead;
214: PartNode * pNext = 0;
215:
216: int New = pPart->GetPartNumber();
217: int Next = 0;
218: itsCount++;
219:
220: if (!pHead)
221: {
222: pHead = pNode;
223: return;
224: }
225:
226: // если это значение меньше головного узла,
227: // то текущий узел становится головным
228: if (pHead->itsPart->GetPartNumber() > New)
229: {
230: pNode->itsNext = pHead;
231: pHead = pNode;
232: return;
233: }
234:
235: for (;;)
236: {
237: // если нет следующего, вставляется текущий
238: if (!pCurrent->itsNext)
239: {
240: pCurrent->itsNext = pNode;
241: return;
242: }
243:
244: // если текущий больше предыдущего, но меньше следующего, то вставляем
245: // здесь. Иначе присваиваем значение указателя Next
246: pNext = pCurrent->itsNext;
247: Next = pNext->itsPart->GetPartNumber();
248: if (Next > New)
249: {
250: pCurrent->itsNext = pNode;
251: pNode->itsNext = pNext;
252: return;
253: }
254: pCurrent = pNext;
255: }
256: }
257:
258: class PartsCatalog : private PartsList
259: {
260: public:
261: void Insert(Part *);
262: int Exists(int PartNumber);
263: Part * Get(int PartNumber);
264: operator+(const PartsCatalog &);
265: void ShowAll() { Iterate(Part::Display); }
266: private:
267: };
268:
269: void PartsCatalog::Insert(Part * newPart)
270: {
271: int partNumber = newPart->GetPartNumber();
272: int offset;
273:
274: if (!Find(offset, partNumber))
275: PartsList::Insert(newPart);
276: else
277: {
278: cout << partNumber << " was the ";
279: switch (offset)
280: {
281: case 0: cout << "first "; break;
282: case 1: cout << "second "; break;
283: case 2: cout << "third "; break;
284: default: cout << offset+1 << "th ";
285: }
286: cout << "entry. Rejected!n";
287: }
288: }
289:
290: int PartsCatalog::Exists(int PartNumber)
291: {
292: int offset;
293: Find(offset,PartNumber);
294: return offset;
295: }
296:
297: Part * PartsCatalog::Get(int PartNumber)
298: {
299: int offset;
300: return (Find(offset, PartNumber));
301:
302: }
303:
304: int main()
305: {
306: PartsCatalog pc;
307: Part * pPart = 0;
308: int PartNumber;
309: int value;
310: int choice;
311:
312: while (1)
313: {
314: cout << "(0)Quit (1)Car (2)Plane: ";
315: cin >> choice;
316:
317: if (!choice)
318: break;
319:
320: cout << "New PartNumber?: ";
321: cin >> PartNumber;
322:
323: if (choice == 1)
324: {
325: cout << "Model Year?: ";
326: cin >> value;
327: pPart = new CarPart(value,PartNumber);
328: }
329: else
330: {
331: cout << "Engine Number?: ";
332: cin >> value;
333: pPart = new AirPlanePart(value,PartNumber);
334: }
335: pc.Insert(pPart);
336: }
337: pc.ShowAll();
338: return 0;
339: }
Результат:
(0)Quit (1)Cat (2}Plane: 1
New PartNumber?: 1234
Model Year?: 94
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 4434
Model Year?: 93
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 1234
Model Year?: 94
1234 was the first entry. Rejected!
(0)Quit (1)Car (2)Plane: 1
New PartNumber?: 2345
Model Year?: 93
(0)Quit (1)Car (2)Plane: 0
Part Number: 1234
Model Year: 94
Part Number: 2345
Model Year: 93
Part Number: 4434
Model Year: 93
Анализ: В строке 82 класс PartsList объявляется другом класса PartNode. В данном случае объявление класса другом происходит в разделе public объявления класса PartNode, но так поступать вовсе не обязательно. Это объявление можно размещать в любом месте объявления класса, что не изменит его суть. В результате объявления класса как друга все закрытые методы и переменные-члены класса PartNode становятся доступными любой функции-члену класса PartsList.
В строке 160 были внесены изменения в вызове функции-члена GetFirst() с учетом появившихся новых возможностей. Теперь вместо возвращения pHead->GetPart эта функция может возвращать закрытую переменную-член pHead->itsPart. Аналогичным образом в функции Insert() можно написать pNode->itsNext = pHead вместо переменной-члена pHead->SetNext(pHead).
В данном случае внесенные изменения существенно не улучшили код программы, поэтому нет особых причин делать класс PartsList другом PartNode. В данном примере просто хотелось проиллюстрировать, как работает ключевое слово friend.
Объявление классов-друзей следует применять с осторожностью. Класс объявляется как друг какого-либо иного класса в том случае, когда два класса тесно взаимодействуют друг с другом и открытие доступа одного класса к данным и методам другого класса существенно упрощает код программы. Однако зачастую проще организовать взаимодействие между классами с помощью открытых методов доступа.