Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лекции / LECS10

.DOC
Скачиваний:
41
Добавлен:
16.04.2013
Размер:
51.71 Кб
Скачать

Статические члены данных

Члены данных могут быть объявлены с использованием модификатора класса памяти static. Член данных, который объявлен как static, разделяется всеми переменными своего класса и хранится в одном месте. Нестатические члены данных создаются для каждого экземпляра класса.

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

имя-класса :: идентификатор

Здесь используется оператор разрешения области видимости. Статический член глобального класса должен быть явно объявлен и определен в области видимости файла.

Например:

class str {

public:

static int how many; //объявление

void print ( );

void assign (const char*);

…..

private: //реализовано в виде символьного

char s [100]; //массива фиксированной длины

};

int str :: how_many = 0 //определение и инициализация

В нашем примере how_many может отслеживать, сколько памяти используется для хранение переменной str. Итак,

str sl, s2, s3, *p;

str :: how_many = 3; //предпочтительнее использовать ::

…..

str t;

t.how_many++; //оператор доступа «точка»

…..

р = new str;

р -> how many++; //оператор доступа через указатель

…..

delete p;

str::how_many--;

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

class ch_stаck {

…..

privatе:

static const int max_len = 10000; //инициализатор

…...

};

const stack :: int max_len; //необходимо объявление

Указатель this

Ключевое слово this обозначает объявленный неявно указатель на себя. Он может использоваться только в нестатических функциях-членах. В статических функциях-членах неявные аргументы недопустимы. Простая иллюстрация использования указателя this приведена ниже.

//Указатель this

class с_pair {

public:

void init (char b) { c2 = 1 + (cl = b); }

с_pair increment ( ) { c1++; c2++; return (*this); }

с_pair* where_am_I( ) ( return this; )

void print ( ) { cout << c1 << c2 << ' \ t'; }

privatе:

char c1, c2;

};

int main ( )

{

с_pair а, b;

a.init ('А');

a.print ( );

cout << " is at " << a.where_am_I ( ) << end l;

b.init ('B');

b.print ( );

cout << “ is at ” << b.where_am_I ( ) << end l;

b.increment ( ). print ( ) ;

}

Функция-член increment использует неявный указатель this, чтобы возвратить приращенные значения с1 и с2. Функция-член where_am_I возвращает адрес за­данного объекта. Ключевое слово this предоставляет встроенный не требующий объявления указатель. Это то же самое, как если бы в c_pair неявно объявлялся закрытый член c_pair* const this. Указатель this нельзя изменить.

Функции-члены типа static и const.

C++ позволяет использовать функции-члены типа static и const. Синтаксически статическая функция-член содержит модификатор static,предшествующий воз­вращаемому типу функции внутри объявления класса. Определение вне класса не должно включать этот модификатор:

class foo {

. . . . .

static int foo_fcn ( ); // сначала — ключевое слово static

. . . . .

};

int foo::foo_fcn ( ) // здесь не должно быть слова static

{ /* определение функции */ }

Синтаксически, функция-член типа const вводится модификатором const, следу­ющим за списком аргументов внутри объявления класса. Определение вне класса также должно включать этот модификатор:

class foo {

. . . . .

int foo_fcn ( ) const;

. . . . .

int foo::foo_fcn ( ) const //необходимо ключевое слово const

{ /* определение функции */ }

Особенности применения функций-членов const и static можно усвоить, используя указатель this. Обычная функция-член, вызываемая как

х.mem ( i, j, k ) ;

имеет явный список аргументов и неявный список аргументов, а именно, список всех членов данных объекта х. Неявные аргументы могут пониматься как данные, доступные через указатель this. В противоположность этому, статическая функция-член не получает неявных аргументов. Постоянная функция-член не может модифицировать свои неявные аргументы. Объявление постоянных функций-членов и постоянных параметров называется контролем постоянства (const-correctness). Контроль постоянства — важное подспорье при написании кода. Благодаря такому контролю можно быть уверенным в том, что компилятор убедится в неизменности значений объектов. Кроме того, контроль постоянства позволяет компилятору производить специальную оптимизацию, например, расположить объект const в памяти только для чтения.

Следующий пример иллюстрирует вышесказанное.

//Вычисление оклада с использованием статических членов

class salary { // оклад

public:

void init (int b) {b_sal = b; your_bonus = 0;}

void calc_bonus (double perc) //вычисление личной премии

{ your_bonus = b_sal * perc; }

//премия для всех сотрудников

static void reset_all (int p) { all_bonus = p; }

int comp_tot ( ) const //суммарный оклад

{ return (b_sal + your_bonus + all_bonus); }

private:

int b_sal;

int you r_bonu s ;

static int all_bonus; //объявление

};

//объявление и определение

int salary :: all_bonus = 100;

int main ( )

{

salary wl, w2;

wl.init (l000) ;

w2.init (2000) ;

wl.calc_bonus (0.2);

w2 .calc_bonus (0.15) ;

salary :: reset_all (400) ;

cout << “ wl ” << wl. comp_tot ( ) << “ w2 ”

<< w2. comp_tot( ) << end l ;

Изменчивость (mutable)

Ключевое слово mutable позволяет членам класса, переменные которого были объявлены как константы, быть, тем не менее, изменяемыми. Таким образом отпадает необходимость отказываться от постоянства, используя конструкцию const_cast<>. Это относительно новая возможность, поддерживаемая не всеми компиляторами С ++. Вот так она применяется:

// Класс с членами mutable

class person { // человек

public:

person (const char*, int, unsigned long);

void bday ( ) { ++ age; } //в день рождения

//увеличиваем возраст

. . . . .

private:

const char* name;

mutable int age;

unsigned long soc_sec;

};

. . . . .

{

// возраст всегда изменяется

const person ira (“ ira pohl ”, 38, 1110111);

. . . . .

ira.bday ( ); //правильно, ira.age — mutable

}

Контейнеры и доступ к их содержимому

Контейнерные классы, такие как стеки и двумерные динамические массивы,— очень полезные типы данных. При этом можно добавить возможность доступа к отдельным членам объектов и их модификации.

//Двумерный динамический массив

class twod {

public:

bool allocate (int r, int s) ; //псевдоконструктор

void deallocate ( ); //псевдодеструктор

double& element_lval(int i, int j) const

{ return base[i][j]; }

double element_rval(int i, int j) const

{ return base[i]tjl; }

int r_size( ) const { return row_size; }

int c_size( ) const { return column_size; }

private:

double** base;

int row_size, column_size;

};

bool twod :: allocate (int r, int s)

{

base = new double*[s];

if (base = = 0) // выделение памяти не выполнено

return false;

for (int i = 0; i < s; ++i) {

base [i] = new double [r];

if (base [i] = = 0) // выделение памяти не выполнено

return false;

}

row_size = r;

column_size = s;

return true;

}

void twod :: deallocate ( )

{

for (int i = 0; i < column_size; ++i)

delete [ ] base [i] ;

delete [ ] base;

row_size = 0;

column_size = 0;

}

Выше определены две почти идентичные функции-члена - element_lval ( ) и element_rval ( ). Они служат для иллюстрации разницы между понятиями lvalue (левостороннее значение или именующее выражение) и rvalue (правостороннее зна­чение). Отличие кроется в возвращаемом типе. Возвращаемый тип double& предпо­лагает, что возвращается ссылка на объект. Так, element_lval ( ) определяет lvalue для манипулирования отдельными индексированными элементами. Функция double element_rval ( ) требует, чтобы было возвращено rvalue индексированных элементов. Изложенное можно проиллюстрировать так:

twod m;

m.allocate (5, 10);

m.element_lval (1, 1) = 5; //поместим 5 в m.base[1][1]

m.element_rval (2, 2) = 6.5; //недопустимое присваивание rvalue

cout << (m.element_rval (1, 1) = =

m.element_lval(1, 1)); //правильно, 5 = = 5

Мы можем написать функцию find_max ( ), которая находит наибольшее значение элемента двумерного объекта:

double find_max (const twod& m)

{

int i , j ;

double max = m.element_rval (0,0) ;

for (i =0; i < m.c_size( ); ++i)

for (j =0; j < m.r_size( ); ++j)

if (m.element_rval(i, j) > max)

max = m.element_rval(i, j);

return max;

}

Заметьте, что element_lval ( ) тоже можно было бы использовать. Теперь давайте напишем алгоритм, транспонирующий двумерную матрицу.

void transpose(twod& m)

{

int i, j;

double temp;

for (i = 0; i < m.c_size ( ) - 1 ; ++i)

for (j = i + 1; j < m.r_size ( ); ++j) {

temp = m.element_lval (i, j);

m.element_lval (i, j) = m.element_lval (j, i) ;

m.element_lval (j, i) = temp;

}

}

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

5

Соседние файлы в папке Лекции