Теоретическая часть
Для начала рассмотрим пример синтаксиса задания класса. Сам синтаксис класса схож с синтаксисом структуры.
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();