Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
lr2.ООП.2015.pdf
Скачиваний:
12
Добавлен:
16.03.2016
Размер:
240 Кб
Скачать

29.Создать класс ШЕСТНАДЦАТЕРИЧНАЯ_СТРОКА. Строки данного класса могут содержать только символы '0', '1', '2', '3','4', '5','6', '7','8', '9','A, 'B','C', 'D', 'E', 'F'. Если в составе инициализирующей строки будут встречены любые символы, отличные от допустимых, ШЕСТНАДЦАТЕРИЧНАЯ_СТРОКА принимает нулевое значение. Добавить возможность заполнения значения строки из числа в двоичной системе.

30.Создать класс БИТОВАЯ_СТРОКА. Строки данного класса могут содержать только символы '0' и '1'. Если в составе инициализирующей строки будут встречены любые симво - лы, отличные от допустимых, БИТОВАЯ_СТРОКА принимает нулевое значение. Добавить возможность сравнения двух строк.

31.Создать класс ШЕСТНАДЦАТЕРИЧНАЯ_СТРОКА. Строки данного класса могут содержать только символы '0', '1', '2', '3','4', '5','6', '7','8', '9','A, 'B','C', 'D', 'E', 'F'. Если в составе инициализирующей строки будут встречены любые символы, отличные от допустимых, ШЕСТНАДЦАТЕРИЧНАЯ_СТРОКА принимает нулевое значение. Добавить возможность объединения двух строк.

32.Создать класс ШЕСТНАДЦАТЕРИЧНАЯ_СТРОКА. Строки данного класса могут содержать только символы '0', '1', '2', '3','4', '5','6', '7','8', '9','A, 'B','C', 'D', 'E', 'F'. Если в составе инициализирующей строки будут встречены любые символы, отличные от допустимых, ШЕСТНАДЦАТЕРИЧНАЯ_СТРОКА принимает нулевое значение. Добавить возможность сравнения двух строк.

33.Создать класс ВОСЬМЕРИЧНАЯ_СТРОКА. Строки данного класса могут содержать только символы '0', '1', '2', '3','4', '5','6', '7'. Если в составе инициализирующей строки будут встречены любые символы, отличные от допустимых, ВОСЬМЕРИЧНАЯ_СТРОКА принимает нулевое значение. Добавить возможность произведения операции побитовой И для двух строк.

Теоретическая часть

Для начала рассмотрим пример синтаксиса задания класса. Сам синтаксис класса схож с синтаксисом структуры.

class ИмяКласса : ИмяРодителя { public:

// в этой секции задаются открытые члены класса ИмяКласса(); // конструктор класса ~ИмяКласса(); // деструктор класса

protected:

//в этой секции задаются защищённые члены класса private:

//в этой секции задаются закрытые члены класса

};

Задание класса начинается с ключевого слова class. За ним мы указываем имя объявляе-

мого класса и базовые классы, если требуются. Далее, описываются элементы класса. Для управления инкапсуляцией используются ключевые слова public, protected и private.

Эти ключевые слова разбивают класс на секции. Идти они могут в любой последовательно - сти. На количество секций также ограничений не распространяется.

Для хранения данных внутри класса используются переменные, для действий класса – функции. Их принято называть полями и методами соответственно.

Также у класса есть два специфических метода: конструктор и деструктор.

Конструктор вызывается при создании экземпляра класса и производит все необходимые действия для его создания . Возможно задание нескольких конструкторов. В этом случае, каждый конструктор должен иметь разные типы аргументов или разное их количество.

Деструктор вызывается при удалении экземпляра класса и должен быть только один. В этом, деструктор является противоположностью конструктору.

Допускается не реализовывать деструктор или конструктор. В таком случае, будет создана

версия по умолчанию.

Начало создания класса

И так, требуется создать класс, который будет реализовывать текстовую строку. Исходя из задания, класс будет хранить строку и реализовывать некоторые операции над ней. Самый простой вариант класса, который можно сделать:

class MyString { public:

char *ptr; int length() {

return strlen(ptr);

}

void cat(MyString v){ strcat(ptr, v.ptr);

}

bool compare(MyString v){ return !strcmp(ptr, v.ptr);

}

};

Посмотрим на использование этого варианта.

MyString s1, s2;

cin>> s1.ptr >> s2.ptr; if(s1.compare(s2))

cout << "Строки равны" << endl;

else

cout << "Строки неравны" << endl; s1.concatenate(s2);

cout << s1.ptr << endl; cout << s1.length() << endl;

При такой реализации класса невозможно задать начальные значения экземпляру. Доработаем наш класс так, что бы можно было задавать начальные значения. Для этого добавим конструкторы в класс:

MyString() {

ptr = new char[1];

}

MyString(char* s){ ptr = new char[1]; strcpy(ptr, s);

}

MyString(MyString s) { ptr = new char[1]; strcpy(ptr, s.ptr);

}

Теперь мы можем использовать класс строки так:

MyString s1, s2("test"), s3(s2); cin>> s1.ptr >> s2.ptr; if(s1.compare(s2))

cout << "Строки равны" << endl;

else

cout << "Строки неравны" << endl; s1.concatenate(s2);

cout << s1.ptr << endl; cout << s1.length() << endl; cout << s2.ptr << endl; cout << s3.ptr << endl;

Замечу, что предыдущий вариант использования был не работоспособен поскольку требуется инициализировать указатель на строку. Что не было сделано в первом варианте класса.

Также интересен организация ввода и вывода строки. Мы на прямую обращаемся к данным

класса. Такая организация делает наш класс чуточку удобнее структуры. Поскольку добавляет функции к данным. Но использование просто указателя на строку текста и реализация набора функций будет не менее эффективным. К этому добавляется небезопасность внутренних данных класса. Проблема безопасности не очевидна в данном классе, но остро всплывает при более сложной структуре данных. При таком подходе нет смысла использовать классы и ООП.

Как можно повысить эффективность класса? Для начала переместим данные в закрытую область. Но в этом случае, мы лишаемся возможности свободного доступа к данным, и, как следствие, вводу и выводу строки. Значит нужно добавить методы для ввода и вывода строки. В результате, класс будет выглядеть следующим образом:

class MyString { char *ptr;

public: MyString() {

ptr = new char[1];

}

MyString(char* s){ ptr = new char[1]; strcpy(ptr, s);

}

MyString(MyString s) { ptr = new char[1]; strcpy(ptr, s.ptr);

}

int length() {

return strlen(ptr);

}

void concatenate(MyString v){ strcat(ptr, v.ptr);

}

bool compare(MyString v){ return !strcmp(ptr, v.ptr);

}

void input(){ cin >> ptr;

}

void output() { cout << ptr;

}

};

Претерпят изменения и способ его использования.

MyString s1, s2("test"), s3(s2); s1.input();

s2.input(); if(s1.compare(s2))

cout << "Строки равны" << endl;

else

cout << "Строки неравны" << endl; s1.concatenate(s2);

cout << s1.length() << endl; s1.output();

s2.output();

s3.output();

Создание эффективных классов

Теперь наш класс можно нормально использовать. Хотя у него есть два недостатка – он не удаляет за собой данные и решает более одной задачи. В принципе, на этом можно успокоится, но давайте рассмотрим следующий пример.

MyString s1, s2("test"), s3=s2;

if(s1 == s2)

cout << "Строки равны" << endl;

else

cout << "Строки неравны" << endl; s3 = s1 + s2;

cout << s1.length() << endl; cout << s1 << endl;

cout << s2 << endl; cout << s3 << endl;

Здесь к классу строки применяются стандартные операции языка. Да и само использование больше походит на применение обычных переменных языка. Такой класс просто использовать даже не зная описания этого класса. Рассмотрим, как нашему классу строки можно добавить такие свойства.

В языке Си++ есть своя специфика создания классов. Она связана с причинами создания и развития языка. Суть специфики такова, что язык не требует от вас строгого создания класса похожего на стандартный тип. Как вы увидели, вы вообще можете использовать класс как расширенный вид структур.

Для создания удобных и эффективных классов применяется идиома ортодоксальной канонической формы класса. Она заключается в разумном расширении функционала класса до возможностей стандартного типа данных. В первую очередь это касается реализации конструкторов, деструктора и операции присвоения.

Посмотрим на реализацию класса строки в соответствии с описной идиомой. Сначала опишем класс:

class MyString { public:

MyString(); MyString(char* s); MyString(MyString s); ~MyString();

MyString operator=(MyString v); MyString operator+(MyString v); MyString operator ==(MyString v); char operator[] (unsigned int i);

int length(); private:

friend istream operator >>(istream i, MyString s); friend ostream operator <<(ostream o, MyString s); char *ptr;

};

istream operator >>(istream i, MyString s); ostream operator <<(ostream o, MyString s);

Как и раньше, класс имеет 3 конструктора и операция получения длины строки. Добавлены деструктор и операция присвоения. И переименованы операции сравнения и конкатенации. Добавлена операция обращения к символу строки. Операции ввода и вывода вынесены из класса и реализованы для использования напрямую с cout и cin.

Вынеся операции ввода и вывода из класса мы делаем его менее зависимым от внешних систем. Такой подход к организации нашего класса позволит использовать его в независимости от применяемых подсистем, в нашем случае способов ввода и вывода.

Конечно, для практического использования нашего класса желательно добавить поддержку стандартной стоки Си. То есть присвоение и сравнение с типом char*, преобразование к этому типу и прочее.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]