- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
- •Оглавление
- •Предисловие
- •Введение
- •1.1. Понятие класса и объекта. Инкапсуляция
- •1.2. Определение классов. Компоненты. Доступность
- •Class_key /*class_id*/ { /*members_list*/ };
- •Value_type class_id::function_id(parameters) {statements}
- •Static cPoint point3(50,120); // статический объект
- •Class_id(parameters) /*:initializer_list*/ {/*statements*/}
- •CPoint point1(100,70); // локальный объект
- •CString(const char *);
- •Delete[] __thematrix;
- •1.4. Обращение к компонентам объектов
- •1.5. Статические и нестатические компоненты классов
- •1.7. Указатель this
- •В опросы для самопроверки
- •2. Механизм наследования. Полиморфизм
- •2.1. Формы наследования. Базовые и производные классы
- •Class_key class_id: inheritance_specifier base_class_id {member_list};
- •2.3. Абстрактные классы
- •2.4. Множественное наследование и виртуальные классы
- •2.5. Преобразование динамических типов. Динамическая идентификация типов
- •Catch ( std::bad_cast & ) { // обработка исключения
- •Return 0;
- •Вопросы для самопроверки
- •3. Дружественные функции и классы
- •3.1. Дружественные функции
- •3.2. Дружественные классы
- •Вопросы для самопроверки
- •4. Механизм вложения
- •4.1. Вложенные классы
- •4.2. Локальные классы
- •Вопросы для самопроверки
- •5. Объектная модель и шаблоны
- •5.1. Определение, описание и инстанцирование шаблонов
- •::Function_id(function_parameter_list) { statements }
- •5.2. Параметры и аргументы шаблонов
- •Class identifier typename identifier
- •// Key, Data – параметры-типы (типы ключа и данных отображения)
- •// Container – контейнер, где содержится информация отображения class сMap {
- •Class MyTemplate
- •Int array[10]; struct Structure { int m; static int sm; } str;
- •5.3. Шаблоны компонентных функций
- •Value_type function_template_id(function_parameter_list) { statements }
- •::Function_template_id(function_parameter_list) { statements }
- •5.4. Специализация шаблонов
- •Вопросы для самопроверки
- •6. Перегрузка операций
- •Value_type operator @ (parameter_list);
- •Value_type operator @ (parameter_list) { statements }
- •Return fail();
- •6.3. Перегрузка бинарных операций
- •Value_type operator @ (parameter); // компонентная функция
- •Value_type operator @ (parameter, parameter); // глобальная функция friend value_type operator @ (parameter, parameter); // дружественная функция
- •Return *this;
- •Return *this;
- •/* Присваиваем собственные данные класса d */
- •6.4. Перегрузка операций управления памятью
- •Typedef void (*new_handler) ();
- •Extern new_handler set_new_handler( new_handler new_p );
- •Void operator delete(void * memory) {
- •... // Специальная обработка пользователя ::operator delete(memory); // освободить память
- •Вопросы для самопроверки
- •7. Механизм исключений
- •Throw expression
- •7.3. Специальные средства поддержки механизма исключений
- •Unexpected_function set_unexpected(unexpected_function func_name);
- •Typedef void (* unexpected_function) ();
- •Extern char * __throwExceptionName; extern char * __throwFileName; extern unsigned __throwLineNumber;
- •Вопросы для самопроверки
- •8. Подсчет ссылок
- •8.1. Назначение механизма подсчета ссылок
- •8.2. Контекстно-независимая модель счетчика ссылок
- •8.4. Внедрение подсчета ссылок в существующий класс
- •Вопросы для самопроверки
- •9. Стандартная библиотека шаблонов (stl)
- •9.1. Назначение и архитектура stl
- •9.2. Последовательные контейнеры
- •Class vector {
- •// Определение итераторов
- •Sort(first,last); // сортировка вектора в диапазоне итераторов
- •Ifstream ifile ("example.In"); ofstream ofile ("example.Out");
- •OutputIterator copy(
- •InputIterator first, InputIterator last, OutputIterator result );
- •// Заполнение списка
- •Operator- (int)
- •Operator- (random access iterator) operator[] (int)
- •InputIterator find(InputIterator first, InputIterator last, const t & value);
- •InputIterator find(InputIterator first, InputIterator last, const t & value)
- •Return first;
- •OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
- •Return result;
- •OutputIterator transform (InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op)
- •Return result;
- •Void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp)
- •__Quick_sort_loop(first, last, comp); __final_insertion_sort(first, last, comp);
- •9.5. Функторы
- •T operator()(const t & X) const { return -X; }
- •T accumulate(InputIterator first, InputIterator last, t init, Function f);
- •V.Push_back(2); V.Push_back(5);
- •9.7. Адаптеры
- •S1.Push(1); s1.Push(5);
- •// Записать в вектор числа 1 2 3 4
- •// Сортировать по неубыванию
- •// Записать в вектор числа 4 6 10 3 13 2
- •Вопросы для самопроверки
- •Заключение
- •Библиографический Список
- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
Return *this;
}
CString & CString::operator = (const char * szSourceString) {
...
Return *this;
}
В результате такой перегрузки появляется возможность присваивать объектам класса CString другие объекты этого же класса или строки const char *.
Если для спецификации возвращаемого значения в operator = использовать что-либо отличное от ссылки на класс (например, void, CString, const CString &), то станут невозможными последовательные цепочки присваиваний, которые допускаются для встроенных типов. Если в функции operator = осуществлять возврат чего-либо, отличного от *this (например, ссылку на аргумент функции), то будет невозможна либо компиляция функции operator =, либо компиляция пользовательского кода.
Еще одна проблема связана с необходимостью отслеживания присваиваний между вложенными компонентами объектов. Если в число компонент класса входят указатели, то необходимо обеспечивать присваивание значений, на которые они ссылаются (функция operator =, определяемая компилятором автоматически, не делает таких присваиваний, копируя лишь сами указатели). При необходимости следует осуществлять перераспределение памяти, занимаемой вложенными компонентами. Ниже приведено определение шаблонного класса, представляющего поименованные указатели (указатели со строковым идентификатором произвольной длины), и дана реализация его функции-операции operator =:
template <class T> class NamedPtr { // поименованные указатели
public:
...
NamedPtr & operator = (const NamedPtr & srcPtr);
~NamedPtr() { delete __actualptr; }
private:
T * __actualptr; // фактический указатель
string __name; // имя указателя
};
template <class T>
NamedPtr<T> & NamedPtr<T>::operator = (const NamedPtr & srcptr)
{
...
__name = srcptr.__name; // копируем имя
__actualptr = new T; // выделяем память под значение
*__actualptr = *srcptr.__actualptr; // копируем значение
// предполагается, что операция T::operator= определена
return *this;
}
Очередная проблема при работе с operator = – это необходимость отслеживания присваиваний между одним и тем же объектом, т.е. когда некоторый объект указан и в левой, и в правой части присваивания. Ее опасность состоит в возможности утечек памяти и, что самое главное, обращения к удаленным динамическим компонентам. Для иллюстрации далее приводится неправильное определение функции operator = для некоторого класса SomeClass:
class SomeClass {
public:
...
SomeClass & operator = (const SomeClass & src);
private:
char * Name;
...
};
SomeClass & SomeClass::operator = (const SomeClass & src) {
delete[] Name; // очищаем память
// выделяем новый «кусок» памяти
Name = new char [strlen(src.Name) + 1];
// Но здесь Name уже не определен!
strcpy(Name,src.Name);
return *this;
}
Поскольку оператор delete[] освобождает память, на которую указывают и this–>Name и src.Name, результат функции strlen(src.Name) не определен. Это и является источником проблемы.
Наконец, последняя проблема в работе с функцией operator = связана с ее использованием в классовых иерархиях. Когда некоторый класс B наследует от класса A, то функция B::operator = должна работать с учетом того, что всякий объект класса B является также и объектом класса A. Соответственно эта функция должна присваивать не только те компоненты, которые были введены в классе B, но и компоненты, унаследованные от класса A; в противном случае данные класса A в объекте останутся неизмененными33. Для решения этой проблемы функция operator = может явно вызывать аналогичную функцию базового класса, чтобы присвоить данные объекта базового класса. Однако такой подход работает не всегда. Ниже представлен другой вариант реализации функции operator = (функция является членом класса D, производного от класса B):
D & D::operator = (const D & srcobj) {
if (this == &srcobj) return *this; // самому себе не присваивать!
/* неявно вызываем B::operator= для присваивания данных класса B */
static_cast<B&>(*this) = srcobj;
