Static-члены (данные) класса
Компоненты-данные могут быть объявлены с модификатором класса памяти static. Класс, содержащий static компоненты-данные, объявляется как глобальный (локальные классы не могут иметь статических членов). Static-компонента совместно используется всеми объектами этого класса и хранится в одном месте. Статическая компонента глобального класса должна быть явно определена в контексте файла. Использование статических компонент-данных класса продемонстрируем на примере программы, выполняющей поиск введенного символа в строке.
#include "string.h"
#include "iostream.h"
enum boolean {fls,tru};
Class cls
{ char *s;
public:
static int k; // объявление static-члена в объявлении класса
static boolean ind;
void inpt(char *,char);
void print(char);
};
int cls::k=0; // явное определение static-члена в контексте файла
boolean cls::ind;
void cls::inpt(char *ss,char c)
{ int kl; // длина строки
cin >> kl;
ss=new char[kl]; // выделение блока памяти под строку
cout << "введите строку\n";
cin >> ss;
for (int i=0; *(ss+i);i++)
if(*(ss+i)==c) k++; // подсчет числа встреч буквы в строке
if (k) ind=tru; // ind==tru - признак того, что буква есть в строке
delete [] ss; // освобождение указателя на строку
}
void cls::print(char c)
{ cout << "\n число встреч символа "<< c <<"в строках = " << k;
}
void main()
{ cls c1,c2;
char c;
char *s;
cls::ind=fls;
cout << "введите символ для поиска в строках";
cin >> c;
c1.inpt(s,c);
c2.inpt(s,c);
if(cls::ind) c1.print(c);
else cout << "\n символ не найден";
}
Объявление статических компонент-данных задает их имена и тип, но не инициализирует значениями. Присваивание им некоторых значений выполняется в программе (вне объекта).
В функции main() использована возможная форма обращения к static-компоненте
cls::ind (имя класса :: идентификатор),
которая обеспечивается тем, что идентификатор имеет видимость public. Это дальнейшее использование оператора разрешения контекста “::”.
Отметим основные правила использования статических компонент:
- статические компоненты будут одними для всех объектов данного класса. То есть ими используется одна область памяти;
- статические компоненты не являются частью объектов класса;
- объявление статических компонент-данных в классе не является их описанием. Они должны быть явно описаны в контексте файла;
- локальный класс не может иметь статических компонент;
- к статической компоненте st класса cls можно обращаться cls::st, независимо от объектов этого класса, а также используя операторы . и -> при использовании объектов этого класса;
- статическая компонента существует даже при отсутствии объектов этого класса;
- статические компоненты можно инициализировать, как и другие глобальные объекты, только в файле, в котором они объявлены.
Указатель this
Как отмечалось выше, если некоторая функция является компонентой объекта, то при вызове этой функции к компонентам-данным этого объекта можно обращаться по имени (опуская имя объекта). Например, пусть имеется объявление двух объектов:
my_class ob1,ob2;
Вызовы компонент-функций имеют вид:
ob1.fun_1();
ob2.fun_2();
Пусть в обеих функциях содержится инструкция:
cout << str;
При объявлении двух объектов создаются две компоненты-данные str. Возникает вопрос, откуда каждая из двух функций узнает, с какой из компонент ей работать (точнее, где она расположена). Ответ состоит в следующем. В памяти для каждого располагаемого объекта создается скрытый указатель, адресующий начало выделенной под объект области памяти. Получить значение этого указателя в компонентах-функциях можно посредством ключевого слова this. Для любой функции, принадлежащей классу my_class, указатель this неявно объявлен так:
my_class *const this;
Таким образом, при объявлении объектов ob1 и ob2 создаются два this- указателя на эти объекты. Следовательно, любая функция, являющаяся компонентой некоторого объекта, при вызове получает this-указатель на этот объект. И приведенная выше инструкция в функции воспринимается как
cout << this->str;
Однако эта форма записи избыточна. С другой стороны, явное использование указателя this эффективно при решении некоторых задач.
Рассмотрим пример использования this-указателя на примере упорядочивания чисел в массиве.
#include<stdio.h>
#include<iostream.h>
class m_cl
{ int a[3];
public:
m_cl srt(); // функция упорядочивания информации в массиве
m_cl *inpt(); // функция ввода чисел в массив
void out(); // вывод информации о результате сортировки
};
m_cl m_cl::srt() // функция сортировки
{ for(int i=0;i<2;i++)
for(int j=i;j<3;j++)
if (a[i]>a[j]) {a[i]=a[i]+a[j]; a[j]=a[i]-a[j]; a[i]=a[i]-a[j];}
return *this; // возврат содержимого объекта, на который
} // указывает указатель this
m_cl * m_cl::inpt() // функция ввода
{ for(int i=0;i<3;i++)
cin >> a[i];
return this; // возврат скрытого указателя this
} // (адреса начала объекта)
void m_cl::out()
{ cout << '\n';
for(int i=0;i<3;i++)
cout << a[i] << ' ';
}
main()
{ m_cl o1,o2; // описание двух объектов класса m_cl
o1.inpt()->srt().out(); // вызов компонент-функций первого объекта
o2.inpt()->srt().out(); // вызов компонент-функций второго объекта
return 1;
}
Вызов компонент-функций для каждого из созданных объектов осуществляется:
o1.inpt()->srt().out;
Приведенная инструкция интерпретируется следующим образом:
сначала вызывается функция inpt для ввода информации в массив данных объекта о1;
функция inpt возвращает адрес памяти, где расположен объект о1;
далее вызывается функция сортировки информации в массиве, возвращающая содержимое объекта о1;
после этого вызывается функция вывода информации.
Ниже приведен текст еще одной программы, использующей указатель this. В ней выполняется добавление строки-компоненты одного объекта к строке-компоненте другого.
#include<iostream.h>
#include<string.h>
class B; // предварительное объявление класса В
Class A
{ char *s;
public:
A(char *s) // конструктор с параметром char*
{ this->s=new char[strlen(s)+1]; // память под строку-компоненту
strcpy(this->s,s);// копирование строки-аргумента в строку-компоненту
}
~A(){delete [] this->s;}
void pos_str(char *);
};
Class B
{ char *ss;
public:
B(char *ss) // конструктор с параметром char*
{ this->ss=new char[strlen(ss)+1]; // память под строку-компоненту
strcpy(this->ss,ss); // копирование строки-аргумента в
} // строку-компоненту
~B(){delete [] this->ss;}
char *f_this(void){return this->ss;}
};
void A::pos_str(char *s)
{ char *dd;
int i,ii;
dd=new char[strlen(this->s)+strlen(s)+2]; //память для перезаписи 2 строк
strcpy(dd,this->s); // перезапись в dd строки объекта a1
dd[strlen(this->s)]=' '; // удаление ’\0’
ii=strlen(this->s);
for(i=0;*(s+i);i++) // дозапись в dd строки объекта b1
*(dd+ii+i+1)=*(s+i);
*(dd+ii+i+1)=0;
delete [] this->s; // удаление строки объекта a1
this->s=dd; // перенаправление указателя на строку dd
cout << this->s << endl;
}
void main(void)
{ A a1("aa bcc dd ee");
B b1("bd");
a1.pos_str(b1.f_this());
}
В результате выполнения программы получим:
aa bcc dd ee bd
Отметим основные правила использования this-указателей:
- каждому объявляемому объекту соответствует свой скрытый this- указатель;
- this-указатель может быть использован только для нестатической функции;
- this указывает на начало своего объекта в памяти;
- this не надо дополнительно объявлять;
- this передается как скрытый аргумент во все нестатические (не имеющие спецификатора static) компоненты-функции своего объекта;
- указатель this - локальная переменная и недоступна за пределами объекта;
- обращаться к скрытому указателю можно this или *this.