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

hkjCJgcQqF

.pdf
Скачиваний:
2
Добавлен:
15.04.2023
Размер:
810.94 Кб
Скачать

списка параметров двоеточием (“:”). Список инициализации членов – это список имен данных-членов, разделенных запятыми, за каждым из которых следует начальное значение, заключенное в скобки. Начальные значения обычно являются параметрами конструктора, которые присваиваются соответствующим данным-членам класса в списке. Например, параметры конструктора len и wid могут быть присвоены данным-членам класса length и width:

Rectangle::Rectangle (float len, float wid) : length(len), width(wid) { } Константные данные-члены класса (имеющие спецификатор const)

инициализируются только в списке инициализации конструктора.

Создание объектов

Один объект может использоваться для инициализации другого в самом объявлении объектов. Например:

Rectangle obj1 (10, 12) , obj2 = obj1, obj3;

Объекты могут свободно присваиваться один другому. Если не создан пользовательский оператор присваивания, присваивание объекта может выполняться побитовым копированием данных-членов класса. Например, obj2 = obj1.

Объекты и передача информации

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

#include <iostream> using namespace std; class MyClass

{

private: double dvar; public:

MyClass (double d); ~MyClass();

void set_dvar (double d) {dvar=d;} double get_dvar () {return dvar; } };

MyClass::MyClass (double d)

{

dvar=d;

cout << “Создание ” << dvar << endl; } MyClass ::~MyClass()

{

13

cout <<”Уничтожение ” << dvar <<endl;

}

void f(MyClass obj); int main()

{

MyClass ob(5.6); f(ob);

cout <<”Это переменная dvar в функции main ”; cout << ob.get_dvar ( ) << endl;

return 0;

}

void f(MyClass obj)

{

obj.set_dvar(7.9);

cout<<” Это локальная переменная dvar”<<obj.get_dvar() <<endl;

}

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

Указатель this

В функции-члене можно непосредственно использовать имена членов того объекта, для которого она была вызвана:

class X { int m;

public:

int readm() { return m; } };

14

void f(X aa, X bb)

{

int a = aa.readm(); int b = bb.readm(); // ...

}

При первом вызове readm() m обозначает aa.m, а при втором - bb.m. У функции-члена есть дополнительный скрытый параметр, являю-

щийся указателем на объект, для которого вызывалась функция. Можно явно использовать этот скрытый параметр под именем this. Считается, что в каждой функции-члене класса X указатель this описан неявно как

X *const this;

и инициализируется, чтобы указывать на объект, для которого функциячлен вызывалась. Этот указатель нельзя изменять, поскольку он постоянный (*const). Явно описать его тоже нельзя, т.к. this – это служебное слово. Можно дать эквивалентное описание класса X:

class X { int m; public:

int readm() { return this->m; } };

Для обращения к членам использовать this излишне. В основном this используется в функциях-членах, непосредственно работающих с указателями. Типичный пример – функция, которая вставляет элемент в список с двойной связью:

class dlink

{

dlink* pre; // указатель на предыдущий элемент dlink* suc; // указатель на следующий элемент public:

void append(dlink*); // ...

};

void dlink::append(dlink* p)

{

p->suc = suc;

 

// т.е. p->suc = this->suc

p->pre = this;

// явное использование "this"

suc->pre = p;

// т.е. this->suc->pre = p

suc = p;

// т.е. this->suc = p

}

 

 

dlink* list_head;

void f(dlink* a, dlink* b)

{

15

// ...

list_head->append(a); list_head->append(b);

}

Массивы объектов

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

Rectangle room [5];

После объявления массива длина и ширина каждого объекта room[i] имеют значения, устанавливаемые конструктором по умолчанию. Чаще всего это нулевые значения:

cout<<room[2].GetLength();

cout<<room[2].GetWidth();

room [2]. PutLength (18); // установка длины комнаты[2] на 18 room[2].PutWidth (16); // установка ширины комнаты[2] на 16

Если класс не имеет конструктора с параметрами по умолчанию, то объявление массива вызовет ошибку.

Множественные конструкторы

Конструктор по умолчанию (default constructor) – это конструктор, не требующий никаких параметров. Это бывает, когда конструктор не имеет параметров или когда каждый параметр имеет значение по умолчанию. С++ позволяет реализовать разнообразные способы инициализации объекта и определять множественные конструкторы в одном и том же классе. Компилятор использует перегрузку функции для выбора правильной формы конструктора при создании объекта. Пример класса, имеющего конструктор с параметрами:

#include <iostream> #include <cstring> using namespace std; class book { private:

char author [40]; char title [40]; int type; public:

book(char * a, char * t, int tp); int get_type ( ) {return type; } void set_type (int tp) { type=tp; } void show ( );

16

};

book::book (char * a, char * t, int tp)

{

strcpy (author, a); strcpy (title, t); type = tp;

}

void book::show ( )

{

cout<< title<<” . “<< author <<” “ <<type<<endl;

}

int main()

{

book b1(“Пушкин А.С. ”, “Евгений Онегин ” , 3); book b2 (“Гоголь Н.В. ”, “Ревизор”, 3);

b1.show ( ); b2.show ( ); return 0;

}

Пример класса с множественными конструкторами: #include <cstdlib>

#include <iostream> #include <string> #include <strstream> using namespace std; class Date

{

private:

// закрытые члены класса date int month, day, year;

public:

// конструктор по умолчанию is January 1, 2000 Date (int m = 1, int d = 1, int y =0);

Date (char *dstr);

// вывод даты в формате "month day, year" void PrintDate (void);

};

// конструктор с параметрами: month, day, year, //-представленными целыми числами

//mm dd yy

Date::Date (int m, int d, int y) : month(m), day(d)

{

year = 2000+ y; // для упрощения выбирается только 21 век

17

}

// конструктор month, day, year представлен строкой "mm/dd/yy" Date::Date (char *dstr)

{

char inputBuffer[16]; char ch;

//копирование в inputBuffer strcpy(inputBuffer,dstr);

istrstream input(inputBuffer, sizeof(inputBuffer));

//чтение из входного потока

//ch используется в качестве символа '/'

input >> month >> ch >> day >> ch >> year; year += 2000;

}

// печать даты с полным названием месяца void Date::PrintDate (void)

{

// allocate static array of month names. Index 0 is NULL string. static char *Months[] = {"","January","February","March","April",

"May","June", "July","August", "September", "October","November","December"};

cout << Months[month] << " " << day << ", " << year;

}

int main(int argc, char *argv[])

{

Date day1(1, 25, 10); Date day2;

Date day3("1/25/10");

cout << "Day Students Tatyana’s day -"; day1.PrintDate();

cout << endl;

cout<<"First day 21 century -"; day2.PrintDate();

cout<< endl; cout<<"text date - "; day3.PrintDate(); cout<<endl; system("PAUSE");

return EXIT_SUCCESS; }

Для построения Date-объектов используются три конструктора: один конструктор со значениями по умолчанию и два конструктора с параметрами. Один из конструкторов с параметрами имеет целочисленные параметры, определяющие значения месяца, дня и года. Второй

18

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

Конструкторы с одним параметром

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

#include <iostream> using namespace std; class X

{

int var; public:

X(int k) {var = k;}

int get_var ( ) { return var; } };

int main ( )

{

X obj = 57; //передает параметру var значение 57 cout << obj.get_var ( );

return 0;

}

Как правило, если конструктор имеет один аргумент, объект можно инициализировать либо с помощью выражения obj (k) , либо с помощью оператора obj = k . Это позволяет неявно выполнять преобразование типа аргумента в тип класса. Чтобы предотвратить неявное преобразование типа аргумента в тип класса, используют ключевое слово explicit, которое помещается перед именем конструктора с одним параметром.

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

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

Объявление статической переменной-члена в классе не означает ее определения, т.е. память для нее не выделяется. Чтобы разместить статическую переменную в памяти, следует определить ее вне класса, т.е.

19

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

#include <iostream> using namespace std; class MyClass

{

static int svar; int ivar; public:

void set(int _svar, int _ivar) { svar = _svar ; ivar = _ivar; } void show ( );

};

int MyClass::svar; // определение статического данного-члена void MyClass::show ( )

{

cout <<”Static svar : ” << svar<<endl; cout <<” Variant ivar : “ << ivar <<endl;

}

int main ( )

{

MyClass obj1, obj2; obj1.set (5, 7); obj1.show ( ); obj2.set (4, 1);

obj2.show ( ); obj1.show ( ); return 0;

}

Статическая целочисленная переменная svar объявлена как внутри класса MyClass, так и вне его. Это необходимо, поскольку объявление переменной svar внутри класса MyClass не сопровождается выделением памяти. Статическая переменная – член возникает до создания первого объекта класса, поэтому ей можно присвоить значение в самом начале программы.

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

20

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

#include <iostream> using namespace std; class MyCount

{

public:

static int counter; MyCount ( ) {counter++; } ~MyCount ( ) { count--;} };

int MyCount::counter; void f ( );

int main ( )

{

MyCount obj1;

cout << “Существующие объекты: ”; cout << MyCount::counter <<endl; MyCount obj2;

cout << “Существующие объекты: ”; cout << MyCount::counter <<endl;

f ( );

cout << “Существующие объекты: ”; cout << MyCount::counter <<endl; return 0;

}

void f ( )

{

MyCount temp;

cout << “Существующие объекты: ”; cout << MyCount::counter <<endl;

}

Как видно, статическая переменная-член counter увеличивается при каждом создании объекта и уменьшается при каждом уничтожении. Следовательно, с ее помощью можно отследить количество существующих объектов класса MyCount.

Статические функции-члены

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

21

могут быть виртуальными. Статические функции нельзя объявлять с помощью ключевых слов const и volatile. Статические функции-члены бывают полезны для предварительной инициализации закрытых статических переменных-членов до создания реальных объектов. Например, в следующей программе статическая функция инициализирует закрытую переменную-член до создания объекта.

#include <iostream> using namespace std; class MyStatFunc

{

private: static int svar; public:

static void init(int st) { svar = st; } void show ( ) { cout << svar; }

};

int MyStatFunc:: svar; //определяем переменную svar int main ( )

{

//инициализация статических данных перед созданием объекта MyStatFunc::init (59);

MyStatFunc X; X.show ( ); return 0;

}

Дружественные функции

С помощью ключевого слова friend можно предоставить обычной функции доступ к закрытым и защищенным членам класса. Для того чтобы объявить дружественную функцию, следует поместить ее прототип внутри класса, указав перед ней ключевое слово friend. Например:

#include <iostream> using namespace std; class MyClass

{

private:

int ivar1, ivar2; public:

friend int sum(MyClass obj); void set_var (int k, int m );

};

void MyClass::set_var ( int k, int m )

{

ivar1 = k;

22

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