Присвоение экземпляров классов 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Присвоение экземпляров классов



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

 

class Class_1 {

public:

int a;

double b;

};

 

Определяем два экземпляра этого класса:

 

Class_1 С1_1, С1_2;

Присваиваем открытым членам экземпляра класса С1_1 некоторые значения:

 

С1_1. a = 1000;

С1_1. b = 3.14;

Теперь присваиваем экземпляру С1_2 значение экземпляра С1_1:

 

С1_2 = С1_1;

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

Присвоение можно выполнять только по отношению к экземплярам одного и того же класса. Например, если определен еще один класс, абсолютно идентичный классу Class_1, и определен экземпляр этого класса С2_1:

class Class_2 {

public:

int a;

double b;

} С2_1;

 

то попытка присвоить экземпляру С2_1 класса Class_2 значения экземпляра С1_1 класса Class_1 приведет к возникновению ошибки. Класса Class_2 и класс Class_1 – несмотря на свою идентичность, разные классы.

 

При выполнении операции присваивания точное копирование данных из одного экземпляра в другой может в ряде случаев привести к достаточно «неприятным» результатам.

Вернемся к нашему примеру класса t_ DinArr. Создадим экземпляр DA1 этого класса, инициализировав его массивом на 100 элементов:

 

t_ DinArr DA1 (100);

При создании этого экземпляра конструктор класса присвоит члену данных ItemCount значение 100, создаст в динамической области массив на 100 элементов и запишет адрес этого массива в член данных Arr этого экземпляра. Пусть, условно, этот адрес равен значению 12345.

Создадим еще один экземпляр DA2 этого класса с массивом на 200 элементов:

 

t_DinArr DA2 (200);

 

Член данных ItemCount этого экземпляра будет содержать значение 200, адрес Arr динамического массива в этом экземпляре пусть будет равен 54321.

Теперь присвоим экземпляру DA2 значение экземпляра DA1:

 

DA2 = DA1;

Поскольку при присвоении осуществляется точное копирование данных, экземпляр DA2 после этого будет содержать те же данные, что и экземпляр DA1. К чему это приведет?

· Во-первых, значение ItemCount экземпляра DA2 станет равно 100, но что хуже всего адрес 200 – элементного массива будет заменен значением адреса 100 – элементного массива из экземпляра DA1. Это приведет к тому, что 200 – элементный массив в динамической области памяти будет «потерян» - произойдет «утечка» памяти.

· Во-вторых, теперь оба экземпляра хранят адрес одного и того же 100 – элементного массива, поэтому любые изменения, сделанные в этом массиве через один экземпляр, будут тут же отражаться и на втором экземпляре. Ни о какой независимой «жизни» этих экземпляров речь идти не может. Они зависимы.

Если необходимы две независимые копии экземпляров, с которыми дальше можно было бы работать абсолютно независимо, то при выполнении такого присвоения нам потребовалось бы:

1) освободить динамическую память от 200 – элементного массива экземпляра DA2;

2) выделить в динамической области памяти пространство для 100 – элементного массива и адрес этого участка присвоить члену Arr экземпляра DA2;

3) скопировать данные из массива экземпляра DA1 в массив экземпляра DA2.

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

 

Передача экземпляров классов в качестве параметров

Конструкторы копий

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

 

Приложения

Приложение 1. Классы. Пример класса с внешней реализацией функций членов (без конструктора и деструктора)

// Class_DinArr_1.cpp: пример класса t_DinArr без встроенных функций и без конструктора и деструктора

//

 

#include "stdafx.h"

#include <iostream>

#include <conio.h>

using namespace std;

 

typedef double t_Inf; // Тип данных элементов массива

const t_Inf DefVal = 0; // Значение элемента массива по умолчанию

char Err [] = "\n***В массиве нет элемента с индексом "; // Сообщение об ошибке

 

/////// Начало описания класса ////////

 

class t_DinArr {

  // Закрытые члены-данные класса

  t_Inf *Arr; // Указатель на динамический массив

  unsigned ItemCount; // Количество элементов в массиве

public:

  // Открытые члены-функции класса

  void Init (unsigned N = 0); // Инициализация массива из N элементов

  void Clear(); // Очистка массива

  void Set (unsigned Ind, t_Inf Val); // Запись значения Val в элемент с индексом Ind

  t_Inf Get (unsigned Ind); // Возвращает значение элемента с индексом Ind

  void Add (t_Inf Val); // Добавляет в конец массива элемент со значением Val

  unsigned Count (); // Возвращает количество элементов массива

  void Free (); // Удаление массива из динамической памяти

};  

 

// ----------------------------------------------

 

void t_DinArr:: Init (unsigned N) // Инициализация массива из N элементов

{

  if (N) // Если N больше 0

        // Выделяем память в динамической области для N элементов массива

               Arr = (t_Inf *) malloc (sizeof(t_Inf) * N);

  else

        // При N = 0 указатель на массив равен 0 – массива нет

        Arr = 0;

  // Устанавливаем значение количества элементов в массиве

  ItemCount = N;

  // Очищаем массив значениями DefVal

  Clear();

}

 

// ----------------------------------------------

 

void t_DinArr:: Clear () // Очистка массива значениями DefVal

{

  for (unsigned i = 0; i < ItemCount; ++ i)

        Arr [i] = DefVal;

}

 

// ----------------------------------------------

 

// Запись значения Val в элемент с индексом Ind

void t_DinArr:: Set(unsigned Ind, t_Inf Val)

{

  if (Ind >= ItemCount) // Индекс выходит за границу массива

        cout << Err << Ind << '!\n'; // Выводим сообщение об ошибке

  else // Индекс правильный

        Arr [Ind] = Val; // Элементу массива с индексом Ind присваиваем значение Val

}

 

// ----------------------------------------------

 

// Возвращает значение элемента с индексом Ind

t_Inf t_DinArr:: Get(unsigned Ind)

{

  if (Ind >= ItemCount) // Индекс выходит за границу массива

  {

        cout << Err << Ind << "!\n"; // Выводим сообщение об ошибке

        return DefVal; // Возвращаем из функции значение DefVal

  }

  else // Индекс правильный

        return Arr [ Ind ]; // Возвращаем из функции значение элемента с индексом Ind

}

 

// ----------------------------------------------

 

// Добавляет в конец массива элемент со значением Val

void t_DinArr:: Add(t_Inf Val)

{

  ++ ItemCount; // Количество элементов в массиве увеличиваем на 1

  // Увеличиваем размер массива до нового количества элементов

  Arr = (t_Inf *) realloc (Arr, sizeof(t_Inf) * ItemCount);

  // В новый (последний) элемент массива записываем значение Val

  Arr [ ItemCount - 1 ] = Val;

}

 

// ----------------------------------------------

 

unsigned t_DinArr:: Count() // Возвращает количество элементов массива

{

  return ItemCount; 

}

 

// ----------------------------------------------

 

void t_DinArr:: Free () // Удаление массива из динамической памяти

{

  if (Arr) // Указатель на массив не равен 0 – массив есть

  {

        free(Arr); // Освобождаем динамическую память

        Arr = 0; // Делаем указатель на массив равным 0 – массива нет

        ItemCount = 0; // Присваиваем количеству элементов в массиве значение 0

  }

}

 

// ----------------------------------------------

 

/////// Конец описания класса ////////

 

int main ()

{

  setlocale (0, "");

  // Описываем экземпляр класса (статическая переменная DA типа t_DinArr):

  t_DinArr DA;

  // Вводим с клавиатуры необходимый размер массива:

  unsigned N;

  cout << "Количество элементов в массиве: ";

  cin >> N;

  // Инициализируем экземпляр класса (при этом в динамической области

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

  // и этот массив будет «обнулён» значениями DefVal):

  DA.Init (N);

  // В цикле заполняем созданный массив некоторыми значениями, например,

  // равными индексам элементов массива:

  for (unsigned i = 0; i < DA.Count (); ++ i)

        DA.Set (i, i);

  // Для контроля выводим на экран значения элементов массива:

  for (unsigned i = 0; i < DA.Count (); ++ i)

        cout << DA.Get (i) << ' '; // на экране видим числа: 0 1 2 … N-1

  cout << endl;

  // В цикле добавляем в конец массива еще 5 элементов, информационные части

  // которых заполняются значениями, от N до N + 4

  for (unsigned i = 0; i < 5; ++ i)

        DA.Add (N + i);

  // Снова для контроля выводим на экран значения элементов массива:

  for (unsigned i = 0; i < DA.Count (); ++ i)

        cout << DA.Get (i) << ' '; // на экране видим числа: 0 1 2 … N + 4

  cout << endl;

  // Заканчиваем работу с массивом (освобождаем динамическую память):

  DA.Free ();   

  system ("pause");

  return 0;

}

 

 

 Приложение 2. Классы. Встроенные функции, конструктор и деструктор

 

// Class_DinArr_2.cpp: пример класса t_DinArr со встроенными функциями с конструктором и деструктором

//

 

#include "stdafx.h"

#include <iostream>

#include <conio.h>

using namespace std;

 

typedef double t_Inf; // Тип данных элементов массива

const t_Inf DefVal = 0; // Значение элемента массива по умолчанию

char Err [] = "\n***В массиве нет элемента с индексом "; // Сообщение об ошибке

 

/////// Начало описания класса ////////

 

class t_DinArr {

  // Закрытые члены-данные класса

  t_Inf *Arr; // Указатель на динамический массив

  unsigned ItemCount; // Количество элементов в массиве

public:

  t_DinArr (unsigned N = 0) { Init(N); } // Конструктор класса

  ~ t_DinArr () { Free(); }         // Деструктор класса

 

  // Открытые члены-функции класса

// ----------------------------------------------

  void Init (unsigned N = 0) // Инициализация массива из N элементов

  {

        if (N)

                      Arr = (t_Inf *) malloc (sizeof(t_Inf) * N);

        else

               Arr = 0;

        ItemCount = N;

        Clear();

  }

// ----------------------------------------------

  void Clear(); // Очистка массива

// ----------------------------------------------

  void Set (unsigned Ind, t_Inf Val) // Запись значения Val в элемент с индексом Ind

  {

        if (Ind >= ItemCount) 

               cout << Err << Ind << '!\n';

        else 

               Arr [Ind] = Val;

  }

// ----------------------------------------------

  t_Inf Get (unsigned Ind) // Возвращает значение элемента с индексом Ind

  {

        if (Ind >= ItemCount)

        {

               cout << Err << Ind << "!\n";

               return DefVal;

        }

        else 

               return Arr [ Ind ];

  }

// ----------------------------------------------

  void Add (t_Inf Val) // Добавляет в конец массива элемент со значением Val

  {

        ++ ItemCount;

        Arr = (t_Inf *) realloc (Arr, sizeof(t_Inf) * ItemCount);

        Arr [ ItemCount - 1 ] = Val;

  }

// ----------------------------------------------

  unsigned Count () { return ItemCount; } // Возвращает количество элементов массива

// ----------------------------------------------

  void Free () // Удаление массива из динамической памяти

  {

        if (Arr) 

        {

               free(Arr); 

               Arr = 0;  

               ItemCount = 0;

        }

  }

};  

 

void t_DinArr:: Clear () // Очистка массива значениями DefVal

{

  for (unsigned i = 0; i < ItemCount; ++ i)

        Arr [i] = DefVal;

}

 

/////// Конец описания класса ////////

 

 

int main ()

{

  setlocale (0, "");

  // Вводим с клавиатуры необходимый размер массива:

  unsigned N;

  cout << "Количество элементов в массиве: ";

  cin >> N;

  // Создаем экземпляр класса (статическая переменная DA типа t_DinArr) с помощью конструктора:

  t_DinArr DA(N);

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

  // 1) t_DinArr DA = N;

  // 2) t_DinArr DA = t_DinArr(N);

 

  // В цикле заполняем созданный массив некоторыми значениями, например,

  // равными индексам элементов массива:

  for (unsigned i = 0; i < DA.Count (); ++ i)

        DA.Set (i, i);

  // Для контроля выводим на экран значения элементов массива:

  for (unsigned i = 0; i < DA.Count (); ++ i)

        cout << DA.Get (i) << ' '; // на экране видим числа: 0 1 2 … N-1

  cout << endl;

  // В цикле добавляем в конец массива еще 5 элементов, информационные части

  // которых заполняются значениями, от N до N + 4

  for (unsigned i = 0; i < 5; ++ i)

        DA.Add (N + i);

  // Снова для контроля выводим на экран значения элементов массива:

  for (unsigned i = 0; i < DA.Count (); ++ i)

        cout << DA.Get (i) << ' '; // на экране видим числа: 0 1 2 … N + 4

  cout << endl;

  system ("pause");

  return 0;

  // При завершении программы локальная переменная DA (экземпляр класса) уничтожается,

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

}

Приложение 3. Классы. Перегрузка конструкторов и функций класса. Дружественные функции

// Пример создания класса для хранения в одном и том же участке памяти разнотипных данных

// Класс использует перегрузку конструкторов и функций членов,

// а также дружественную функцию

 

#include "stdafx.h"

#include <iostream>

using namespace std;

 

const int SLen = 11;

const char Err[] = "\n*** Нет значения ***\n";

 

class t_Variant

{

  char TypeVal; // Тип значения (0 - нет значения; 1 - целое; 2 - вещественное; 3 - строка)

  union {       // Анонимное объединение для хранения значений разных типов

        int IVal;

        double DVal;

        char SVal [SLen];

  };

public:

  // Перегруженные конструкторы

  t_Variant() { TypeVal = 0; }; // Нет значения

  t_Variant(int V) { Set(V); }; // Инициализация целым

  t_Variant(double V) { Set(V); }; // Инициализация вещественным

  t_Variant(char V[]) { Set(V); }; // Инициализация строкой

  // Перегруженные функции члены

  void Set(int V) // Присвоение целого значения        

  { TypeVal = 1; IVal = V; }

  void Set(double V) // Присвоение вещественного значения        

  { TypeVal = 2; DVal = V; }

  void Set(char V[])  // Присвоение строки        

  { TypeVal = 3; strncpy_s(SVal, SLen, V, SLen - 1); }

  // Остальные функции члены

  int ToInt() // Получение целого значения

  { if (TypeVal == 1) return IVal; else {cout << Err; return 0;} }

  double ToDouble() // Получение вещественного значения

  { if (TypeVal == 1 || TypeVal == 2) return DVal; else {cout << Err; return 0;} }

  char * ToString() // Получение строки

  { if (TypeVal == 3) return SVal; else {cout << Err; return "";} }

  friend char * ConvToStr(t_Variant V, char * Dest); // Дружественная функция

};

 

char * ConvToStr(t_Variant V, char * Dest) // Дружественная функция

{

  switch (V.TypeVal)

  {

        case 0: strcpy_s(Dest, 100, Err);

                      break;

        case 1: _itoa_s(V.IVal, Dest, 15, 10);

                      break;

        case 2: _gcvt_s(Dest, 20, V.DVal, 10);

                      break;

        case 3: strcpy_s(Dest, 100, V.SVal);

                      break;

  }

  return Dest;

}

 

int _tmain(int argc, _TCHAR* argv[])

{

  setlocale(0, "");

  t_Variant Var1; // Создаем пустой экземпляр Var1

  cout << Var1.ToInt() << endl; // На экране: *** Нет значения *** и 0

  Var1.Set(10); // Записываем в экземпляр Var1 целое значение

  cout << Var1.ToInt() << endl; // На экране: 10

  Var1.Set(3.14); // Записываем в экземпляр Var1 вещественное значение

  cout << Var1.ToDouble() << endl; // На экране: 3.14

  Var1.Set("This is text"); // Записываем в экземпляр Var1 строку с усечением

  cout << Var1.ToString() << endl; // На экране: This is te

  t_Variant Var2(6.28); // Создаем экземпляр Var2 с вещественным значением

  cout << Var2.ToDouble() << endl; // На экране: 6.28

  t_Variant Var3("Text"); // Создаем экземпляр Var3 с текстовым значением

  cout << Var3.ToString() << endl; // На экране: Text

  // Использование дружественной функции ConvToStr:

  Var3.Set("Друг"); // Записываем в Var3 новое значение

  char S[100];

  cout << ConvToStr(Var3, S) << endl;

  cout << S << endl;

  system("Pause");

  return 0;

}

 

Приложение 4. Классы. Еще один пример

 

 

///////////////////////////////////////////////////////////////////////////////////////////

// Пример реализации класса для работы с одномерными  //

//                    однонаправленными списками.             //

// В этой реализации элементы списка индексируются     //

//                               начиная с 0.                                      //

///////////////////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"

#include <iostream>

using namespace std;

///////////////////////////////////////////////////////

// Начало определения класса List //

///////////////////////////////////////////////////////

// Тип данных t_ Inf определяет тип данных информационных частей

// элементов динамического списка. В этом примере в качестве

// информации, хранящейся в элементах списка, используется тип данных double.

// Если необходимо хранить в элементах списка данные другого типа,

// необходимо заменить тип double,

// на нужный тип данных. При этом необходимо скорректировать

// значение определенной ниже константы DefVal.

// Также, возможно, потребуется незначительная коррекция функций-членов

// класса, связанная с операциями с информационными частями элементов списка

typedef double t_ Inf;

// Значение информационной части элементов списка, используемое по умолчанию

const t_ Inf DefVal = 0;

// Структура t_ Item определяет тип данных элемента списка

struct t_ Item

{

t_ Inf Inf;       //Информационная часть - хранит полезную информацию

t_ Item * Adr; // Адресное поле - хранит адрес следующего элемента списка

};

Class List

{

// Закрытые члены-данные класса

t_ Item * Items,                  // Адрес первого элемента

       * End;                    // Адрес последнего элемента

unsigned Count;               // Количество элементов в списке

// Закрытые члены-функции класса

bool ItemExists(unsigned Index); // Проверка наличия элемента по его индексу

t_ Item * Item(unsigned Index);   // Получение адреса элемента по его индексу

t_ Item * ExtractItem(unsigned Index); // Выделение элемента из списка по его

                                                                  // индексу

public:

// Открытые члены-данные класса

// Открытые члены-функции класса

List(unsigned N = 0); // Коструктор класса. N - количество элементов списка

~ List(void);              // Деструктор класса

void Init(); // Инициализация пустого списка

void Create(unsigned N = 0); // Создание нового списка из N элементов

void Clear();             // Удаление элементов списка

unsigned ItemCount(); //Возвращает количество элементов в списке

t_ Inf GetItem(unsigned Index); // Получение информационной части элемента

                                                        // по его индексу

void PutItem(unsigned Index, t_ Inf Val); // Присвоение значения Val элементу

                                                                      // с индексом Index

void AddItem(t_ Inf Val = DefVal); // Добавление элемента в конец списка

                                                               // (Val - значение информационной части)

void DelItem(unsigned Index); // Удаление из списка элемента с индексом Index

void InsItem(unsigned Index, t_Inf Val = DefVal);    // Вставка нового элемента

                                                                                        // в позицию Index

void MoveItem(unsigned OldIndex, unsigned NewIndex); // Перестановка элемента

                                                                                                   // с индексом OldIndex

                                                                                                   // на позицию NewIndex

};

// ----------------------------------------------

List:: List (unsigned N)

// Конструктор класса List. N - количество элементов в списке.

// Автоматически  вызывается при создании объекта

{

// Вначале список пустой:

Init ();

// Создаем в динамической области список из N элементов:

Create (N);

}

// ----------------------------------------------

List:: ~ List ()

// Деструктор класса List. Автоматически вызывается при уничтожении объекта

{

// При уничтожении объекта освобождает динамическую память от списка

Clear ();

}

// ----------------------------------------------

Void List::Init()

  // Инициализация пустого списка

{

Count = 0;

Items = 0;

End = 0;

}

// ----------------------------------------------



Поделиться:


Последнее изменение этой страницы: 2021-09-26; просмотров: 46; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.117.196.217 (0.282 с.)