Created
October 28, 2011 18:13
-
-
Save ramntry/1322950 to your computer and use it in GitHub Desktop.
OOP start
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Реализация не-inline методов класса. Для обращения к внутреннему пространству имен класса необходимо | |
// использовать оператор разрешения области видимости имен :: с предваряющим его именем | |
// класса. | |
#include "intarray.h" | |
IntArray::IntArray(int n) : | |
m_size(n), // Инициализация полей класса методом прямого | |
m_array(new int[n]) // ... вызова конструкторов этих полей (встроенные | |
{ // ... типы в C++ тоже имеют свои конструкторы) | |
for (int i = 0; i < n; i++) // Более сложные действия по инициализации экземпляра осуществляют | |
m_array[i] = 0; // ... в теле конструктора. | |
// (new в случае неудачи выбрасывает исключение std::bad_alloc, потому | |
// ... нет необходимости сверять m_array на равенство нулю, что пришлось | |
// ... бы делать при работе с malloc) | |
} | |
int& IntArray::operator [](int index) // Возвращать этот метод должен именно ссылку на индексируемый элемент | |
{ // ... нашего массива, поскольку ее можно использовать и для чтения | |
if (index < 0) // ... значения - справа от оператора присваивания, и для записи в | |
index += m_size; // ... элемент массива - слева от оператора присваивания. Иначе говоря, | |
// ... a[2] = 5; развернется в a.operator [](2) = 5; что будет работать как | |
if (index < m_size && index > -1) // ... a.m_array[2] = 5; только если поток выполнения дойдет | |
return m_array[index]; // ... до этой (<-) строки | |
// Мы реализуем индексацию по отрицательному индексу, где -1-ый элемент - последний | |
throw OutOfBoundsException(); // ... с конца. Если после корректировки индекса он все равно вне допустимого диапазона | |
} // ... значений, бросаем исключение. | |
std::ostream& operator <<(std::ostream& out, const IntArray& self) // Реализация дружественной функции (обратите внимание, без IntArray::) | |
{ // ... со стандартной для оператора << в библиотеке вывода iostream | |
for (int i = 0; i < self.m_size - 1; i++) // ... сигнатурой и поведением. Друг хорошо знает, как вы устроены и не | |
out << self.m_array[i] << ' '; // ... попытается вам навредить. Потому ему можно разрешить напрямую работать | |
out << self.m_array[self.m_size - 1]; // ... с полем m_array не тратя усилий на проверку выхода за границы | |
// ... массива или поддержку не использующихся здесь отрицательных индексов | |
return out; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// В заголовочный файл следует поместить интерфейс класса - все то, что непосредственно | |
// понадобиться пользователю класса, а также реализовать все подстановочные (inline) методы, | |
// поскольку после выполнения директив include их тела окажутся в одном файле с использованиями, | |
// что для inline-функций критично и, конечно, здесь нужно задать структуру класса - все его поля. | |
#pragma once | |
#include <iostream> | |
class OutOfBoundsException {}; // Пустой класс достаточно функционален в роли исключения: | |
// ... он имеет конструктор по умолчанию и можно создавать | |
// ... его экземляры, также он обладает вполне говорящим именем. | |
// ... (Очень полезно обратиться сюда: | |
// ... http://www.cplusplus.com/reference/std/exception/exception/) | |
class IntArray | |
{ // Функции-друзья не являются методами класса, потому неплохо | |
friend std::ostream& operator <<(std::ostream& out, const IntArray& self); // ... указать их вне секций класса (хотя на самом деле "вне | |
// ... секции" под ключевым словом class то же, что и в секции | |
// Со спецификатором "друг" указывают функции, которым разрешен доступ // ... private - но для друзей нет никакой разницы, где они | |
// ... к приватным членам класса, но эти члены не внедряются в локальную // ... объявлены. Другое дело struct - там секция по | |
// ... область видимости таких функций - потому им нужно явно передвать // ... умолчанию - public) На практике друзей помещают | |
// ... экземляр класса для работы с его членами. // ... в конец объявления класса - подальше от глаз. | |
public: | |
IntArray(int n); // (впрочем, будет не лишним указать конструктор как explicit. См. сноску) | |
~IntArray() // Методы, тело которых объявлено в теле класса могут быть только | |
{ delete[] m_array; } // ... inline - прямое указание этого спецификатора ничего не изменит. | |
// ... Не стоит делать inline'овыми функции длиной более, чем в 1-2 строки. | |
int& operator [](int index); // Конструкция some[i] эквивалентна some.operator [](i). Подробнее см. *.cpp | |
int size() const // Метод size не меняет объект - неплохо это явно отметить спецификатором const | |
{ return m_size; } | |
private: | |
int const m_size; // Префикс m_ - всего лишь неплохое решение двух проблем - обхода совпадения имен методов | |
int* const m_array; // ... и полей класса, а также выделение полей класса среди других переменных (параметров | |
// ... и локальных переменных) в телах реализаций методов. Оба поля приватны и недоступны | |
// ... пользователю, он не может их прочитать или изменить. Более того, оба поля объявлены | |
// ... константными, что позволяет установить их значение лишь раз за все время жизни объекта, | |
// ... что здесь оправдано - наш массив не умеет менять свой размер. (Обратите внимание, что | |
// ... константен указатель на массив - его не настроить на другой массив. Но сами элементы | |
// ... массива легко могут быть изменены) Важно, что инициализировать константные поля вы сможете | |
// ... только из конструктора. | |
}; | |
/* | |
По поводу поведения ключевого слова explicit и конвертирующего конструктора все очень хорошо сказано | |
тут: http://alexeivasin.com/post/explicit-cpp-keyword.aspx | |
*/ | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
HEADERS += \ | |
intarray.h | |
SOURCES += \ | |
main.cpp \ | |
intarray.cpp | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include "intarray.h" | |
using namespace std; | |
int main(void) | |
{ | |
IntArray a(10); // Явный вызов конструктора | |
cout << "After declaration: " << a << endl; // Здесь вызывается независимая от класса, но дружественная | |
// ... ему функция operator << | |
for (int i = 1; i <= a.size(); i++) // Протестируем доступ по отрицательным индексам | |
a[-i] = i; | |
cout << "After initialization: " << a << endl; | |
// Здесь осуществляется операция доступа за пределы | |
// try // ... массива, что приводит к выбросу объектом соответствующего | |
// { // ... исключения. Обратите внимание на сообщение об исключении в | |
cout << a[a.size()] << endl; // ... окне консоли и на то, что программа была немедленно завершена. | |
// } | |
// catch (OutOfBoundsException) // Эта ветка try-блока (похожего на switch, где catch - аналог case'а, | |
// { // ... в данном случае фиксирующего тип исключения) обрабатывает | |
// cerr << "Out of bounds exception" << endl; // ... исключение выводом сообщения в поток ошибок. Раскомментируйте | |
// } // ... try-блок и посмотрите, как это работает. Обратите внимание на то, | |
// ... что программа нормально продолжает работу и прощается с вами | |
cout << "Bye" << endl; | |
return 0; | |
} | |
/* С выходом из блока, в котором был создан экземпляр класса (внимание, не указатель на экземпляр, размещенный | |
в куче, а сам экземпляр - в автоматической области памяти (на стеке) - а именно так и был создан a), он, как | |
и все остальные локальные переменные, уничтожается - но стираются не только поля класса (здесь - m_size и m_array), | |
(это как раз поведение деструктора по умолчанию) но и вызывается пользовательский деструктор, который в данном | |
случае освобождает память в куче вызовом delete[] для указателя, ранее инициализированного операцией new в | |
конструкторе нашего класса. Если бы и сам экземпляр создавался в куче (например, так: | |
IntArray* pa = new IntArray(10); | |
), то для него нужно было бы вызвать delete (delete pa;), тогда память, отведенная под поля m_size и m_array, | |
на этот раз в куче, была бы освобождена - а вот уже в связи со смертью этих полей, как и в прошлом случае, | |
вызвался бы деструктор. То есть здесь вручную вызывается только деструктор по умолчанию, а пользовательский | |
им (!) опять-таки вызывается автоматически) | |
*/ | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment