<< Предыдущая Оглавление Следующая >>


6.5. Перегрузка операторов

Классы в языке С++ предоставляют возможность переопределять работу некоторых операторов таких как ‘=’, ‘+’, ‘*’, ’new’, ‘delete’, ‘==’, ‘>’, ‘<’ и др. Это дает возможность упрощать текст программы и повышать качество программирования. Преимущества от перегрузки операторов хорошо проявляются при работе со строками, которые в базовом языке С++ представляют собой массивы символов и операции присваивания, добавления и сравнения строк становятся сложными. Однако, используя классы можно сделать работу со строками подобно работе с обычными переменными.

Зададим класс string, в котором будут описаны алгоритмы обработки строковой информации:

class string
{
public:
string() {buff[0]='\0';}
string(const char* str) {strcpy(buff,str);}
~string() {}

char* GetString() {return buff;}
void SetString(const char* str) {strcpy(buff,str);}

private:
char buff[MAX_LENGTH];
};

В данном классе описано два конструктора для возможности создания пустой и некоторой начальной строк. Кроме того, задано два метода: GetString() и SetString(), через которые осуществляется доступ к массиву buff[], являющимся частным элементом класса. Представленный класс описывает минимальный набор функций для работы со строками. С помощью него можно лишь задать какую-либо строку и получить ее, например, для вывода на экран. Все возможные манипуляции со строками с помощью класса string демонстрирует текст программы, представленный ниже:

string str;
str.SetString(“Hello World”);
printf(“%s”,str.GetString());

При этом операции присваивания одной строки другой, добавления, сравнения строк между собой и т.п. остаются нереализованными. Формально для выполнения этих операций можно задать набор функций в классе string, но ими будет менее удобно пользоваться, чем общеизвестными операторами ‘=’, ‘+’и ‘==’. Для того чтобы сказать, что должен делать конкретный оператор при работе с классом string, его нужно перегрузить, т.е. определить алгоритм, который будет выполняться при реализации того или иного оператора.

Перегрузка оператора выполняется с помощью ключевого слова operator, за которым следует символ оператора, затем в круглых скобках аргумент, т.е. то, что предполагается будет стоять справа от оператора. После этого в бигурных скобках задается алгоритм, который будет выполняться при вызове перегруженного оператора. Пример перегрузки оператора ‘=’ для присваивания строки показан ниже:

class string
{
public:
string() {buff[0]='\0';}
string(const char* str) {strcpy(buff,str);}
~string() {}

char* GetString() {return buff;}
void SetString(const char* str) {strcpy(buff,str);}

void operator = (const char* str) {strcpy(buff,str);}

private:
char buff[MAX_LENGTH];
};

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

После перегрузки оператора присваивания с классом string становятся возможны следующие действия:

string str;
str = “Hello World”;

Здесь при выполнении операции присваивания активизируется алгоритм, записанный в теле оператора, а именно функция strcpy(), в которой указатель str указывает на строку символов, стоящих справа от оператора присваивания. Таким образом, происходит копирование переданной строки в массив buff класса string.

Следует также отметить, что описанное действие оператора присваивания распространяется только на класс string. Это значит, что при выполнении, например, операций над числами:

int a = 10;
int b = a;

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

Особенностью перегрузки данного оператора является то, что он будет работать только в том случае, когда его правый аргумент является массивом символов и не будет выполняться в случае присваивания одного класса string другому:

string str, dst(“Hello”);
str = dst; //работать не будет

Вместе с тем такая запись вполне логична и естественна и желательно чтобы ее можно было использовать наряду с простыми массивами. Это достигается путем добавления в класс string еще одной реализации перегрузки оператора присваивания, следующим образом:

class string
{
public:
string() {buff[0]='\0';}
string(const char* str) {strcpy(buff,str);}
~string() {}

char* GetString() {return buff;}
void SetString(const char* str) {strcpy(buff,str);}

void operator = (const char* str) {strcpy(buff,str);}
void operator = (string& str) {strcpy(buff,str.GetString());}

private:
char buff[MAX_LENGTH];
};

В итоге, при реализации алгоритма

string str, dst(“Hello”);
str = dst;

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

В представленном примере перегрузки аргументом является ссылка на класс string, а не представитель этого класса. Это сделано, для того чтобы при реализации оператора присваивания не происходило копирование класса string при передаче аргумента и, таким образом, экономилась память ЭВМ и увеличивалась скорость работы. Поэтому, формально, программа будет работать, если вместо ссылки использовать представитель класса.

Следующим шагом оптимизации работы со строками выполним перегрузку оператора добавления одной строки другой, которая будет определяться символом ‘+’. Для этого, по аналогии, добавим в класс string следующие строки:

void operator + (const char* str) {strcat(buff,str);}
void operator + (string& str) {strcat(buff, str.GetString();}

который возможно использовать только таким образом:

string str;
str + “Hello”;

Это несколько непривычная запись оператора добавления строки. Было бы правильнее использовать запись вида

string str;
str = str + “Hello”;

но здесь используется сразу два оператора: присваивания и добавления. Поэтому для реализации такой конструкции необходимо, чтобы оператор добавления ‘+’ возвращал сформирванную строку оператору присваивания. Следовательно, описание оператора добавления в классе string должно выглядеть так:

char* operator + (const char* str) {return strcat(buff,str);}
char* operator + (string& str)
{
return strcat(buff, str.GetString();
}

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

bool operator == (const char* str)
{
if(strcmp(str,buff) == 0) return true;
else return false;
}

bool operator == (string str)
{
if(strcmp(str.GetString(),buff) == 0) return true;
else return false;
}

В результате получаем следующее описание класса string:

class string
{
public:
string() {buff[0]='\0';}
string(const char* str) {strcpy(buff,str);}
~string() {}

char* GetString() {return buff;}
void SetString(const char* str) {strcpy(buff,str);}

void operator = (const char* str) {strcpy(buff,str);}
void operator = (string& str) {strcpy(buff,str.GetString());}

char* operator + (const char* str) {return strcat(buff,str);}
char* operator + (string& str)
{
return strcat(buff, str.GetString();
}

bool operator == (const char* str)
{
if(strcmp(str,buff) == 0) return true;
else return false;
}

bool operator == (string str)
{
if(strcmp(str.GetString(),buff) == 0) return true;
else return false;
}

private:
char buff[MAX_LENGTH];
};

который можно использовать следующим образом:

int main()
{
string str;
str = “Hello World”;
string dst;
dst = dst + str;

if(str == dst) printf(“src is equal dst”);
else printf(src is’nt equal dst”);

return 0;
}


<< Предыдущая Оглавление Следующая >>