Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

членов самого разнообразного назначения но два типа функций занимают особое положение

Работа добавлена на сайт samzan.net:


PAGE  23

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

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

 

       Для многих объектов естественно требовать, чтобы они были инициированы (т.е. имели начальное значение, а не мусор) до начала их использования.

        В  С++  для упрощения процесса инициализации объектов предусмотрена специальная функция, называемая конструктором.

        Конструктор – это компонентная функция, вызываемая автоматически при  создании объекта класса и выполняющая необходимые инициализирующие  действия.

  1.  Основное назначение – инициализация объектов.

      

-  инициализация данных класса - задание им начальных

        значений  программно или по умолчанию,

     - открытие файлов,

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

  1.  Имя конструктора должно совпадать с именем класса.

3) Функция-конструктор не может возвращать результат, даже тип void не

    допустим.

 

4) Функция автоматически вызывается при определении объекта, или при       размещении в памяти объекта с помощью операции new.

  1.  Формат определения конструктора в теле класса:

                 < имя класса> ( список формальных параметров)

                         { операторы тела конструктора }

   

        Конструктор, как и любая компонентная функция, может быть определен     и вне тела класса, имея в теле класса прототип.

       class T  {

                           …

                         public:

                         T ( список параметров) ;

                          …

                        } ;

         T::T( список параметров) { тело конструктора}

  1.  Как правило, конструкторы объявляются в открытой части класса
  2.  Конструктор может отсутствовать, при создании экземпляров  класса

     компилятор  автоматически выделяет под них память, хотя в этом случае

    данные не инициализируются, и будут содержать мусор.

8) В определении класса могут присутствовать несколько конструкторов.

   

     Деструктор - это функция, которая автоматически выполняется, когда экземпляр класса уничтожается.

( либо при выходе объекта за пределы своей области видимости, либо при уничтожении динамического объекта операцией  delete).

 

       Назначение – выполнение завершающих действий (напр., закрытие файлов, установка видеосистемы в исходное состояние, написание каких-либо фраз   и т.д.)

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

        Класс может иметь несколько конструкторов, но деструктор может быть только один.

Формат компонентной функции-деструктора

          ~ имя класса ( ) { операторы тела деструктора};

 

  1.  Между тильдой и именем класса нет пробелов.
  2.  У деструктора нет типа результата даже void и нет параметров даже типа void.
  3.  Деструктор выполняется неявно, автоматически, как только объект уничтожается. Его, как правило, никогда не вызывают, но можно и вызывать явно, если он определен в классе

            <имя объекта>. ~ <имя класса> ( );

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

Рассмотрим класс Men, несущий в себе данные о возрасте и имени индивидуума.

      Пусть в этом классе присутствуют и конструктор и деструктор.

      В конструкторе кроме инициализирующих действий имеется еще вывод контрольной строки.

      Деструктор пусть также содержит вывод другой контрольной строки.

class Men  {

 char* name ;

 int age;

public:

 Men(char * n, int a)        //встроенный конструктор

{     name = n;   age = a;

 cout<<name <<  “  - begin “<<endl;

 }

void  SetN   (char*n) {name =n ; } // комп. функция для изменения данного

void SetA (int a) { age  = a ;} // комп. функция для изменения другого данного

char* GetN ( ) { return name; } // компонентные функции

int GetA { return age; }                  //возвращающие значения данных

~Men ( ) { cout<< name<< “- end”<<endl;}     // деструктор

#include <iostream.h>

#include<conio.h>

void main () {

/* создание экземпляра класса по имени:

конструктор автоматически вызывается при создании объекта, и т.к. наш конструктор требует двух параметров, то в строке создания объекта     (или в строке вызова конструктора) необходимо указать оба параметра, которые и инициируют создаваемый объект m1 */

Men   m1 (“Петров” , 34 ) ;        

                   /* создается объект,     параметры конструктора инициализируют

                      переменную m1   (объект) */

                    /*   Men m1 ; - уже не верно!

                       т.к. в классе нет конструктора без параметров,  т.е. в этом

                        случае нельзя создать неинициализированный объект * /

/* создание экземпляра класса с помощью указателя:

параметры конструктора  инициализируют динамически выделенный участок памяти под объект типа Men*/

Men * m2 = new  Men ( “Рощин ”, 25 ) ;

cout<<m1. GetN ( )<< “ “ << m1. GetA( )  << endl;

cout<< m2->GetN( ) << “ “ << m2-> GetA ( ) << endl;

getch( );

}

Результат:

Петров  -begin

Рощин  - begin

Петров  34

Рощин  25

Петров  -end

     Итак, конструктор вызывался автоматически дважды при создании объекта по имени  и с помощью указателя.

   Деструктор вызывался только один раз. Для объекта m2 деструктор не вызывался, т.к. мы выделили память “вручную”  и так и не вернули ее системе, т.е. система не фиксирует уничтожение этого объекта при завершении программы.

Если перед getch()  вставить строку

delete (m2);

то результат будет:

Петров  -begin

Рощин  - begin

Петров  34

Рощин  25

Рощин  -end

Петров  -end

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

 

   Назначение конструктора инициализировать данные, однако данные могут приобретать значения и другим способом с помощью компонентных функций SetN( ) и  SetA( ).

     Конструктор мог бы инициализировать только одно данное  объекта и выглядеть, напр., так

Men (char*n){ name =n;}

    А возраст определялся бы каким-то другим способом.

Еще примеры конструктора  с параметрами:

#include <iostream.h>

struct book {

    char* name ;

float price;

 book (char* newn, float newpr)

 { name=newn; price=newpr;};

 };

void main()

{ book a1 ("Хождение по мукам",34.5);

cout<<a1.name;

}

#include <iostream.h>

#include <string.h>

 struct goods {

      char name [40];

float price;

 goods (char* newn, float newpr)

 {

             // name=newn    - ошибка      (lvalue  required )

              strcpy(name,newn); price=newpr;};

 };

 void main()

{ goods b1 ("Шляпа",12.5);

 cout<<b1.name;

}

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

Заключение:

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

1)                 Определение статического экземпляра класса:

<имя класса>   <имя объекта> ( параметры конструктора);

Пустой список параметров не допустим, если в классе  конструктор с параметрами 

2)             Определение массива статических экземпляров класса:

<имя класса>   <имя массива>  [размер массива] =

        { <имя класса>( параметры конструктора для  0-го экземпляра), …,

      <имя класса> ( параметры конструктора для последнего экземпляра)

        };

3)                Определение динамического экземпляра класса:

<имя класса> *  <имя указателя на объект> = new <имя класса> ( параметры конструктора);

4)              Определение массива динамических экземпляров класса:

а)

<имя класса> * <имя массива указателей на объекты> [разм. массива] = { new <имя класса> ( параметры конструктора для  0-го экземпляра), …,     new<имя класса>( параметры конструктора для последнего экземпляра) };

б)

cin>>n;           // количество экземпляров

<имя класса>**<имя двойного указателя> = new <имя класса>*[n];

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

<имя двойного указателя>[i] =new <имя класса>   ( параметры

                                                                    конструктора для i-го экземпляра);

Можно создавать безымянный объект при инициализации другого объекта

  

 <имя класса > (фактические параметры конструктора);      // нет имени

                              {

                                   

                        goods tip5 ( “Пальто” , 20000);    //обычно

                                                   

                       good tip6 = good ( “Шуба” , 100000);

// создается безымянный  объект, значения которого используются, напр., в //инициализации вновь созданного объекта tip6    

                                 }

Конструктор с аргументами, задаваемыми по умолчанию

          Этот конструктор позволяет при его вызове с параметрами инициализировать данные создаваемого объекта указанными значениями параметров.

         Но возможен  вызов конструктора (или создание объекта) и без параметров, при этом  данные инициализируются   значениями по умолчанию.

  Для класса Men конструктор с умалчиваемыми значениями параметров:

Men (char*n = “ ”, int a =0)

 // значения по умолчанию имя – пробел, возраст -0

 {name=n; age = a; }

/* Такой конструктор можно вызвать без параметров. Если такой конструктор вызвать без параметров, то создаваемый объект будет инициироваться значениями по умолчанию.

       Вызов того же  конструктора с параметрами будет инициировать данные создаваемого объекта значениями параметров */

void main ()

{   Men m1;            // name = “ “   ,  age  =0

  Men m2 ( “ Иванов”, 45)     // name =”Иванов”,  age = 45

  Men m3  (“Петров”)              // name= “Петров”  age=0

  Men m4 (18)                           // ошибка!

    Нельзя задавать параметр, перескакивая через умалчиваемое значение (это значение компилятором будет трактоваться как значение параметра n, и естественно будет сообщение об ошибке, т.к. это не строка).

   Напр., если четыре параметра с умалчиваемыми значениями, можно вызывать конструктор:

  1.  без параметров;
  2.  с указанием только первого параметра ( три параметра – значения по умолчанию);
  3.  с указанием двух первых параметров ( два последних – по умолчанию)
  4.  с указанием трех первых (  последний – по умолчанию)
  5.  с указанием четырех параметров

 

Еще пример:   

… struct mag

               { int a,b, c ;

                  mag ( int aa=1, int bb=2 , int cc=3) // конструктор

                                                    //с умалчиваемыми значениями параметров

                   {   a= aa; b=bb; c= cc; };

                   void shownumber ( void)

                   {cout << a <<b <<c ; } ;

                    };                                          

         mag one;                     // объект инициализируется значениями 1, 2 ,3

         mag one1 (10);            // объект инициализируется значениями 10, 2 ,3

         mag one2 (10,20);      // объект инициализируется значениями 10, 20 ,3

         mag one3 (10,20,30)  // объект инициализируется значениями 10, 20 ,30

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

    

          Men ( char*n, int a=0) { name = n; age = a; }

   

    Men ( char*n = “ “ ,   int a) { name = n; age = a; }  //  ошибка!

Конструктор по умолчанию

          Это разновидность конструктора без параметров (присутствует в классе в единственном экземпляре).

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

         Этот конструктор не имеет параметров.

 

 class  A  {

       int x , y ;

       public:

       A ( ) ;

       ...

              };

    A  :: A  ( )              // невстроенный конструктор  по умолчанию

  {   x  =0 ; y = 0;        // инициализация данных, может отсутствовать

     ...                           // другие инициализирующие действия

   }

  /* мог бы быть просто такой:

   A :: A ( ) {  }          */

  При наличии конструктора по умолчанию , предложение

    A  one ;     

 создает объект со значениями данных  x=0  и  y=0.

 

     Если в конструкторе не было инициализации, то этот конструктор предоставляет возможность создавать неинициализированные объекты даже при наличии в определении класса еще одного  конструктора с параметрами.

 

        Конструктор по умолчанию схож при вызове с конструктором с умалчиваемыми значениями и при написании конструкции:

 <Имя класса>   < имя объекта> ;

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

    

     При наличии в классе конструктора с параметрами, задаваемыми по умолчанию, объявлять конструктор по умолчанию нельзя!

      Если в определении класса вообще нет конструктора, компилятор автоматически предоставляет конструктор по умолчанию следующего вида:

 

<Имя класса>  ( ) {  }

который и создает неинициализированные объекты.

Конструктор копирования

Назначение:

  1.  создать объект  полностью совпадающий с уже созданным;

  1.  передача в некоторую функцию экземпляра класса, при этом в стеке создается локальная копия объекта, с которым и работает функция;

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

     Операции копирования объектов выполняет конструктор копирования.

В большинстве случаев компилятор предоставляет нам конструктор копирования по умолчанию, обеспечивая правильное копирование.

Копирование по умолчанию:

class T {

     int x, y ;

      public:

   T ( int tx , int ty )  {  x  =  tx ;  y = ty ;  }

    int  GetX ( )  { return  x;  }

    int  GetY ( )   { return  y; }

 friend  T  sum (  T,  T);           

};

void Print ( T obj )  

{ cout<<’\n’<<”x=”<<obj.GetX( ) <<”   y=”<<obj.GetY( ); }

T   sum (  T  obj1 , T obj2 )

{   obj1. x  + =obj2. x ;

    obj1. y  + =obj2. y ;

   return  obj1 ; }

#include <iostream.h>

void  main () {

 T  e1 ( 1, 10);      //создали объект, вызвав конструктор с параметрами

Print ( e1 );                          //  первое копирование

 T e2 = e1;                                // второе копирование

 Print ( e2 );                              // третье копирование

 T e3 = sum ( e1, e2 ) ;               // четвертые копирования

 Print (e3);                                  // пятое копирование

 }

// выведется:

// x= 1   y = 10

  x= 1   y = 10

  x= 2   y = 20

Определение конструктора копирования

    Конструктор копирования должен иметь для данного класса такой вид:

 

1.   T ( T obj )  { x = obj.x  ;   y = obj. y }       //   не верно!

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

       Рекурсию можно избежать, если не копировать объект в конструктор, а использовать передачу параметра по ссылке:

2.        T ( T & obj )  { x = obj.x  ;   y = obj. y }  

       И последнее – необходимо ввести запрет на модификацию копируемого параметра  для этого используется  ключевое слово const:

3.     T ( const T & obj )  { x = obj.x  ;   y = obj. y }  

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

Рассмотрим, как происходит копирование.

  Как удостоверится,   что

- при копировании объекта,

- при передачи объекта в функцию,

                        -при возвращении функцией объекта в некоторую

                         переменную (также объект)

вызывается конструктор копирования  по умолчанию, или конструктор копирования явно определенный в классе?

    

Для того, чтобы зафиксировать факт вызова конструктора копирования, мы правильное копирование (в программе написанной выше, конструктор варианта 3 вызывается по умолчанию)  заменим на копирование с приращением:

class T {

     int x, y ;

      public:

   T ( int tx , int ty )  {  x  =  tx ;  y = ty ;  }

   T (const T & obj) { x = obj.x+1  ;   y = obj. y +1 }  

 

    int  GetX ( )  { return  x;  }

    int  GetY ( )   { return  y; }

 friend  T  sum (  T,  T);           

};

void Print ( T obj )  

{ cout<<’\n’<<”x=”<<obj.GetX( ) <<”   y=”<<obj.GetY( ); }

T   sum (  T  obj1 , T obj2 )     // хотя закрытые данные функция -друг

{   obj1. x  + =obj2. x ;          //  имеет право обращаться к компонентам

    obj1. y  + =obj2. y ;

   return  obj1 ; }

#include <iostream.h>

void  main () {

 T  e1 ( 1, 10);                     //создаем объект

 Print ( e1 );                          //  первое копирование - приращение  

 T e2 = e1;                                // второе копирование

 Print ( e2 );                              // третье копирование

 T e3 = sum ( e1, e2 ) ;               // четвертые копирования

 Print (e3);                                  // пятое копирование

 }

// выведется:

// x= 2   y = 11

  x= 3   y = 12

  x= 7   y = 25

  1.  При копировании  e1 в локальную переменную obj  в функции Print,

   в  данные объекта obj передадутся значения x и  y объекта  e1  с

   инкрементом,  которые  и    будут  выведены     функцией  (x = 2    y = 11)

  1.  При копировании в e1 в  e2 , в  объект e2 передадутся

    инкрементированные значения x и y объекта e1    ( 2   и 11- значения

    данных  объекта e2).

  1.  Затем при вызове Print(e2)  , значения объекта e2 копируются в локальную

   переменную obj  в функции Print.    В obj передадутся значения x и  y  из

   e2 с инкрементом они и будут выведены ( x= 3     y = 12).

4) Затем в локальные переменные obj1   obj2 функции sum  копируются

   данные объектов e1  e2  с инкрементом  в  obj1 - (2  11),   в  obj2 -  ( 3  12).

   Локальные объекты суммируются в функции. Результирующий объект

   имеет значения данных   ( 5   23 ).

   Данные этого объекта копируются с инкрементом в данные объекта e3 в

    выражении  T  e3 = sum (…); т.е. e3 получает  данные ( 6   24 ).

5) При вызове Print(e3)  , значения объекта e3 копируются в локальную

   переменную obj  в функции Print.    В obj передадутся значения x и  y  из

   e3 с инкрементом они и будут выведены ( x= 7     y = 25).

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

  

   Обязательное его определение в тех случаях, когда класс содержит поля, являющиеся указателями на динамические участки памяти.

#include <iostream.h>

#include <conio.h>

class  My {

              int* p;

             public:

            My ( int  a)   // встроенный конструктор с параметром

           { p= new  int ;     *p = a ; }

           My ( const My & obj )  //конструктор копирования с инкрементом

             { p = new  int ; *p = *obj.p+1 ; }

  

   /* Для фиксации вызова конструктора  копирование также проводится с

      инкрементом.

      Конструктор копирования, создавая копию объекта, сначала  выделяет

      память под данное- член нового объекта и затем присваивает ему

      значение , взятое из объекта – аргумента. Правильное присваивание

      ( без инкремента):     *p = *obj.p;

      */                                     

           int Get ( ) { return * p } // возвращает  значение по адресу указателя

           

         ~My ( ) { delete (p);  }   // деструктор память, выделенную под данное

                                               // объекта освобождает

                 } ;

        

         void Print ( My obj )  // функция вывода на экран самой переменной

        { cout<< ‘\n’ <<  obj.Get ( ) ;}

         void main ( )

       { My  a1 (10) ;   // создается объект, переменная инициализируется 10

         My a2 = a1; // с помощью конструктора копирования создается объект

                             // a2, со значением переменной 11, т.к. в конструкторе

                            // инкремент

         Print ( a1) ;

        /* значение  переменной объекта a1 копируется с инкрементом в

          в переменную локального объекта obj функции (11 ) и это

           значение выводится */

         Print ( a2 ) ;

        /* значение  переменной объекта a2 копируется с инкрементом в

          в переменную локального объекта obj функции (12 ) и это

           значение выводится */

          a1. ~My( ) ;   // вызовом деструктора объект память a1 освобождается

         Print ( a1) ;   // выведется мусор

         Print ( a2);   // выведется 12

           }

      Результат:

       11

       12

       7853

       12  

 После вызова деструктора a1 освобождается динамическая память и  в данном (*p) – члене  объекта a1 содержится теперь мусор (операция delete в процессе возврата в систему возвращаемой памяти затирает эту память).

 Объект a2 остался неизменным. Так и должно быть.

  

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

10

10

7853

7853

 

Уничтожение объекта a1 привело к уничтожению и его копии a2 , в которой находится теперь тот же мусор.

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

    При отсутствии конструктора копирования программа будет использовать конструктор  копирования по умолчанию следующего вида:

 

My (  My & obj ) { p = obj.p; }

      В объекте a1 находится указатель на выделенное обычным конструктором поле памяти.

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

         

              Объект a1

                                                              Данное (*p) 

                    Объект

 a2

При  освобождении памяти в   a1 ( или  в a2 ) стирается наше единственное данное, т.е. и второй объект разрушается.

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

    

  Объект a1

                                                       Данное  ( *p)

       

   Объект a2

                                                        Данное (*p)

           В этом случае объекты  a1 и a2 будут указывать на разные поля. Уничтожение одного из них вызовом деструктора,  никак не отразится на существовании другого. Именно это и выполняется в нашем конструкторе копирования.

                                           Еще о конструкторах

Есть еще один  способ инициализации объекта с помощью списка    инициализаторов  данных объекта.

 Этот список помещается при описании конструктора  между списком параметров и телом конструктора:

      

            

           <имя класса> ( список параметров) : < список инициализаторов>

                                                { тело конструктора }

Пример :

Class A

        { int ii  ;  float ee ;    char cc;

          public :   A( int i ,  float e , char c ) :    ii ( 7),

                                                                           ee( ii + i * e),

                                                                            cc(c)

                       {  }

                        . . .

          };

        A a( 5 , 1.2 . f’)  ; //  создается объект с компонентами a.ii =7 ,

                                          // a.ee = 13 ,  a.cc=’f’

-   Параметром конструктора не может быть его собственный класс, но

    может быть ссылка на него.

-  В классе может быть несколько конструкторов, но только один с

    умалчиваемыми значениями параметров.

-  Нельзя получить адрес конструктора.

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

<Имя класса> < имя объекта>;

Если есть хоть один конструктор с параметрами (но без значений по- умолчанию), для того чтобы иметь возможность создать неинициированный объект надо объявить в теле класса конструктор по умолчанию:

<Имя класса> ( ) { };

тогда используя конструкцию

<Имя класса> < имя объекта>;

можно объявить неинициированный объект.

Примеры:

1.    

сlass Book {

             public :

             char title[40];

            char author[20];

             float price;

            Book ( char*atitle, char*aauthor, float aprice );       //прототип

                                                                                             //конструктора

            ~Book( ) ;               // прототип деструктора

           void show_book(void) { cout<<’\n’<< title<< “,  “<<price ;}

                            } ;

          Book::Book(char*atitle, char*aauthor, float aprice )

             {           strcpy(title, atitle);    strcpy(author, aauthor);

                          price = aprice; }

          Book::~Book( )

          { cout <<’\n’<< “Уничтожение экземпляра:”<< title ;}

 

         void main ( )

             { Book tip1 (“Turbo Pascal”, “B.B.Фаронов”, 60.0) ;

                Book tip2(“Язык С++” , “В.В.Подбельский”,62.0 );

               

           Tip1.show_book( );

           Tip2.show_book( );

               }

   Turbo Pascal , 60.0

   Язык С++ , 62.0

   Уничтожение экземпляра : Язык С++

   Уничтожение экземпляра : Turbo Pascal

 

  1.  class stroka  {

               char*ch    // указатель на строку на символьный массив

              int len    // длина строки

            public:

        stroka( char*cch)              // конструктор1

          { len = strlen(cch) ; ch=new char [len+1];

              strcpy (ch,cch); } ;

        

         stroka(int N=20)              //  конструктор 2

        { ch= new char[N+1}; len=0; ch[0]=’\0’}

        

         int  len_str(void) { return len;}       // возвращает  длину 

                                                                   //строки

         char * string (void) {return ch;} // возвращает указатель на строку

        void vivod ( ) // выводит данные

        { cout<< “строка: “ <<ch <<” ,  длина строки=”<<len;};

       ~stroka( )                            // деструктор

       { delete []ch ;} ;

 }

 void main ( )

              { stroka S1( “Миру-мир”);

                    S1.vivod;}

Деструктор автоматически освободит память.

Деструктор идеальное место для всей общей работы по очистке, напр.: освобождение памяти, сохранение данных в файлах , закрытие файлов…

                             

                                    Компонентные данные

  1.  При обращении к  данным из компонентных функций используется только

имя компонента

  1.  При обращении к компонентам из операторов, выполняемых вне  класса

( если позволяет статус доступа)

                 имя объекта. имя компонента

         указатель на объект -> имя компонента

  1.  Данные класса (а также компонентные функции) не обязательно должны быть описаны  до первого обращения к ним в классе. Т.е. все компоненты класса видны во всех операторах его тела.

В связи с этим отличием введена особая область видимости – класс (наряду с файлом , блоком и  функцией ).

  1.  Каждый объект класса имеет свою копию данных класса, кроме    статических данных.  Статический компонент (static) не тиражируется,     существует в единственном экземпляре

   

    Статические компоненты   класса  необходимо описывать и   инициировать

    вне определения класса как глобальные переменные.

  

   Обращаться   к   ним   можно   до   определения  первого   объекта   класса с

   помощью квалифицированного имени:

имя класса ::имя компонента

    

   Если      объекты      объявлены, то    к   статическому  компоненту   можно

 обращаться  стандартно, и  всякое  изменение  статического  компонента   в одном объекте становится видно во всех объектах.

   

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

               имя класса ::имя статической функции (параметры )

     Пример:

                        Class cot

                               { …;

                                       static int N;// статический компонент

                                    public:   …

                                      static void count(int value) { N = value} ;

                                  };

                         int cot::N=0; // внешняя инициализация статического элемента

      

                      void main ( )

                     {      cot::count(500); …   

                        //изменение статического компонента до объявления   объектов

                       }

  1.       Указатели на компоненты класса

Определение указателя на компонентные данные класса:

тип данных(имя класса ::*имя указателя)

В определение можно включить и инициализацию

               & имя класса :: имя компонента

int ( stroka::*plen)=&stroka::len;

        //  -  не верно, т.к. lenимеет статус private

При инициализации надо использовать общедоступные компоненты.

После объявления объекта к его компонентам следующий формат обращения через указатель:

    

имя объекта.* указатель на компонент данных;

Если определен указатель на объект формат обращения:

Указатель на объект-> *указатель на компонент данных ;

 

Определение указателей на компонентные  функции

  Тип возвращаемого результата ( имя класса ::*имя указателя на метод)

                                               ( спецификация параметров функции);

    

void (book::*fprt)(void) = & book::show_book ;

 // определение указателя на компонентные функции класса book  и

// инициализация указателя компонентной функцией show_book

 И форматы обращения к методам через указатели:

Имя объекта.*указатель на метод(параметры);

Указатель на объект-> *указатель на метод ( параметры);

Пример:  

book A (“C и C++”, “Б.И.Березин, 40.0); // определен объект 

void (book::*fprt)(void) = & book::show_book ;

   (A.*fptr) ( ) // выведет на печать значения объекта

Если  в определении класса было  бы несколько идентичных  функций указатель fptr можно “настроить” на другие функции

Компонентные функции

Если определение функции полностью размещено в классе, то эта функция по умолчанию считается подставляемой, т.е. при каждом вызове код функций встраивается в точку вызова.

При внешнем определении в теле класса располагается прототип функции:

 

<Тип> <имя функции >(спецификация формальных параметров);

Вне тела класса компонентная функция определяется следующим образом:

<Тип ><имя класса>::<имя функции>(спецификация формальных

                                                                                                    параметров)

                        {  тело функции   };          

Функция компонент класса имеет туже область видимости, что и класс.

Пример:          Работа с графической библиотекой

При объявлении методов класса  использовались (инкапсулировались) графические функции, прототипы которых  находятся в заголовочном файле graphics.h

       Все эти функции предоставляют возможности управления графическим экраном.

      Стандартное состояние ПК – соответствует работе экрана в текстовом режиме (25 строк по 80 символов в строке).

      Если мы хотим использовать графические средства компьютера, необходимо инициировать графический режим работы дисплейного адаптера.

     Для управления техническими средствами ПК  имеются соответствующие программы, называемые драйверами.

     Графический драйвер управляет дисплейным адаптером в графическом режиме.

     Графические возможности адаптера определяются разрешением экрана  (количеством точек экрана) и количеством цветов, которыми может светиться каждая точка.

Наиболее распространенные дисплейные адаптеры (CGA - Color Graphics Adapter, EGAEnhanced (усиленный) Graphics Adapter,  VGAVideo Graphics Array (графический видеомассив),  SVGA и т. д. )  могут иметь несколько графических режимов работы. Для управления  современными графическими адаптерами мы используем драйвер EGAVGA.BGI

       Экран представляет собой совокупность светящихся точек - пикселей. Количество точек определяется монитором и режимом драйвера для работы с ним. Положение пикселя определяется его координатами по отношению к точке с координатами 0, 0 – верхнему левому углу экрана.

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

                             

initgraph( тип графического драйвера, режим адаптера , путь к драйверу)

void  initgraph  (int far * graphdriver, int far * graphmode, char far * pathtodriver);

Для указания типа драйвера в файле имеются константы:

DETECT = 0  (режим автоопределения типа)

CGA =1

EGA=3

VGA=9

и т. д.

Аналогично имеются константы для определения моды адаптера.

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

    

 int  dr = DETECT ,                  // тип драйвера и  

          mod ;                               // режим работы адаптера

                                                  // определяются автоматически

                     //режим при этом выбирается с максимальным разрешением

    initgraph ( &dr , &mod ,   "D:\\Borlandc\\BGI" );

// предполагается, что драйвер находится в каталоге BGI

#include <graphics.h>

#include <conio.h>    //содержит прототип функции getch( )

// опишем класс

    class  point {        //точка на экране дисплея

                    int x, y ; // собственные компонентные данные

                    public: // общедоступные компонентные функции

                    point (int xx=0 , int yy =0) ; // прототип конструктора

                                                        // с умалчиваемыми значениями

                     

                    void show (void); // прототип функции изображения точки

                     

                    void move  (   int xo=0 ,   int yo =0) ; // прототип функции 

                     // перемещения точки с умалчиваемыми значениями

                   private :  // собственная функция класса

                   void hide ( ) ; // прототип функции, убирающей точку с экрана

                  } ;

        //дадим внешнее определение методам класса

 

   point::point( int xx=0,  int yy=0 ) // определение конструктора

        {  x =  xx  ; y = yy ;  }

        void point :: show (void)   

        { putpixel (x, y , getcolor( ) ) ;}     //   int getcolor(void)

                                                                //возвращает номер цвета символов

       void point :: hide(void)

       { putpixel (x, y , getbkcolor( )) ; }

      void point ::move ( int xn=0,  int yn=0 )

       { hide ( ) ;

               x =xn  ;  y= yn ;

          show ( ) ; }

      void main ( )

     { // создается три объекта , три невидимых точки

        point A ( 150, 70 );

        point B ;     //  координаты по умолчанию равны x=0  и y=0

        point D ( 400, 200);

       //инициализация графики

        int dr = DETECT , mod ;

        initgraph ( & dr , & mod ,   “D :\\ borlandc\\ bgi “);

       //установка цвета

        setcolor(4) ;

        A.show ( ) ;                  // показать на экране точку А

        getch ( )                      // подождать до нажатия клавиши

        B.show ( ) ;                // показать на экране точку В

        getch ( )  ;               // подождать до нажатия клавиши

        D.show ( ) ;            // показать на экране точку D

        getch ( )  ;               // подождать до нажатия клавиши

       A.move ( ) ;            // переместить точку А в начало координат

       getch ( )  ;               // подождать до нажатия клавиши

       B.move(50,  60 ) ; // переместить точку B

       getch ( )  ;               // подождать до нажатия клавиши

       closegraph( ) ;

}

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

С этой же целью используется функция  int kbhit (void)   (прототип в файле conio.h ) .

 while (! Kbhit() ); - задержка программы до нажатия клавиши.

Контрольные вопросы:

  1.  В чем суть объектно-ориентированного подхода к программированию? Дать определение класса.

  1.  Определение объекта класса по имени и с помощью указателя и динамического выделения памяти

  1.  Обращение к компонентам объекта

  1.  Что такое статический элемент класса

  1.  Что такое друзья класса

  1.  Что такое конструкторы и деструкторы. Какие типы конструкторов вы знаете?

  1.  Когда необходимо описывать конструктор копирования?

      

Указатель this

        Каждый раз при создании объекта класса строится указатель, называемый this, и содержащий адрес этого объекта. Правильнее сказать , указатель определяется  не в момент создания объекта , а в момент вызова любого из методов объекта.

        В связи с неявным определением this является константным указателем, т.е. по умолчанию происходит определение:

    имя класса *const this = адрес обрабатываемого объекта

При работе с компонентами класса можно использовать указатель this

                                                                                        Эквивалентно:

Class  point  { int x, y ;                                            Class  point  { int x, y ;

                     public:                                                            public:

                     point( int xx=0, int yy=0)                    point( int xx=0, int yy=0)

                 {this-> x=xx ; this ->y =yy  ;} ;                       { x=xx ;     y =yy  ;} ;

                 void print ( void)                                          void print ( void)

            { cout<< this->x <<”  “ << this->y;} ;              {cout<< x <<”  “ <<y;} ;

                              }                                                                        }

    В таком использовании нет никаких преимуществ.

    Иногда используется  при конфликте имен , когда имена формальных параметров функций совпадают с именами компонентов класса:

Class  point  { int x, y ;                                            Class  point  { int x, y ;

                     public:                                                            public:

                     point( int x=0, int y=0)                    point( int x=0, int y=0)

                 {this-> x=x ; this ->y =y  ;} ;                { point::x=x ; point::y =y  ;} ;

               // используя this                          // используя квалифицированное имя

                                                              …   

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

Class A

{ int x, y ;

public:

A ( int xx=0, int yy =0){ x=xx ; y = yy; }

 A  func ( ) ;

}

A A :: func( ) { if ( x%2) x++;  // функция x преобразующая в четное

                       return * this;

                       }

void main ()

{  A a1 (17, 55)

    A a2 = a1 . func ();

}

/* Можно объявить так

A*  A:: func ( )

{ …

 return this}   */

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

Пример:      Очередь   

#include <iostream.h>

#include<stdio.h>

Class  que

       { static que*first ; // указатель(адрес)первого элемента очереди

                que*next ; // указатель на следующий элемент очереди

                char bukva;  //содержимое элемента очереди

                public:    // общедоступные функции

               que(char c)  { bukva = c } ;       // конструктор

                void add (void) ; // функция добавления элемента в очередь

                static void print (void); // вывод содержимого очереди

          }

        // определения функций:  

         void que::add(void)

           { que* list= first ; // вспомогательный указатель устанавливается на

                                         //   начало очереди

             que * uk  ;  // вспомогательный указатель для продвижения по

                                 //очереди

        while(list!=NULL) { uk = list ; list=list->next }//продвижение по очереди

        if( uk!=NULL) {uk->next=this; }//присоединение  в конец очереди

          else first = this;                 // очередь пустая

         this->next=NULL; }

           void que::print (void)

      { que *list = first ;     // устанавливаем на начало очереди

          if ( list = = NULL) {cout << “ список пуст”; return; }

            else cout<<”содержимое списка :”

          while( list!=NULL)

               { cout<< list->bukva; list= list->next; }//выводим и продвигаемся по 

                                                                                //  очереди

      }

           que *   que :: first = NULL; //инициализация статического компонента

      

      void main( )

{  //формируем объекты класса

 que A( ‘a’) ;  que B(’b’ ) ;    queC(‘c’);     queD(‘d’);

que::print( );                                     // выводим фразу, что список пуст

     A.add( );      B.add( );      C.add( );     D.add( );//включаем в список   

                                                                                 // элементы

 que::print( ); }                    //выводим список   




1. а. В психологии различают два вида агрессии- инструментальную и враждебную.
2. Использование факторинга в финансовой деятельности предприятия.html
3. франц Empire империя от лат
4. соц исследований По дисциплине Методология и методика
5. реферат дисертації на здобуття наукового ступеня кандидата юридичних наук.html
6. Тема- Фізіологія збудливих тканин Плазматична мембрана Зовнішня об
7. Интеллект будущего по следующим номинациям- Математика
8. Тема- Лидерство в системе менеджмента
9. ПОЛИТИЧЕСКАЯ ПСИХОЛОГИЯ
10. Проблемы источниковедения истории психологии
11. Противодействие торговле людьм
12. Лекция 6 Защита данных Проблема защиты данных актуальна т
13. Тема 1- ПОНЯТТЯ ФАБРИК ДУМКИ ЇХ РОЛЬ І МІСЦЕ В СУЧАСНОМУ СУСПІЛЬНОПОЛІТИЧНОМУ ЖИТТІ 1
14. 2012 гг. Школьный этап 78 классы Участник школа класс
15. сказуемое главного предложения стоит в одной из форм прошедшего времени то глаголсказуемое придаточного
16. Реферат Государственная политика в отношении насилия в семье
17. 2007 Cоставители- дмн профессор Ю
18. Женщины Геринга
19. Дипломная работа- Психологический синдром детей младшего школьного возраста
20. 2 Q ВЫБОР 2 ФАЙЛ Сумма ряда равна