Конструкторы копирования.
Копирующий конструктор позволяет производить копирование одного объект класса в другой объект того же класса. Эта операция необходима в следующих случаях:
Вновь создаваемый объект класса должен стать копией уже имеющегося объекта (инициализация копированием).
Два объекта класса созданы ранее, нужно скопировать один объект в другой, например при выполнении операции присваивания.
Если аргумент функции или возвращаемое значение является объектом класса, то при передаче параметров и возвращаемых значений неявно вызывается конструктор копирования.
Если программист не задает явно конструктор копирования, то компилятор создает его автоматически. В этом случае действуют правила копирования по- умолчанию, то есть копируются все нестатические поля класса.
В следующем примере работает копирование по-умолчанию.
#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
using namespace std;
class MyStr
{ ...
...
};------- конец определения класса MyStr
int MyStr::count=0;
int MyStr::num=0;
int main(int argc, char* argv[])
{ MyStr c1("1_string"); // создание объекта с1
cout<<endl<<"Объект с1"<<endl;
c1.display();
MyStr c2(c1); // создание объекта с2 (инициализация копированием)
cout<<"Объект с2"<<endl;
c2.display();
MyStr c3=c1; // создание объекта с3 и присвоение ему значения с1
cout<<"Объект с3"<<endl;
c3.display();
system ("pause");
return 0;
}
Строка MyStr c2(c1) вызывает конструктор копирования (во вновь создаваемый объект с2 записывается информация из с1). В строке MyStr c3=c1 оператор присваивания вызывает конструктор копирования.
Анализируя результаты работы можно сделать вывод, что копирование по-умолчанию корректно работает с нестатическим полем buf, статические же поля никак не изменяются.
Для того, чтобы получить правильный результат, необходимо определить копирующий конструктор, который правильно работает со всеми полями класса.
Добавим в класс MyStr копирующий конструктор:
MyStr(MyStr& s)
{buf= new char[strlen(s.buf)+1];
num++;
count++;
nn=num;
strcpy(buf, s.buf);
}
В результате программа полностью корректно работает.
В копировании принимают участие два объекта класса, причем один объект активизирует копирование, а другой играет пассивную роль.
MyStr c2(c1); - активный объект с2, пассивный с1
MyStr c3=c1; - активный объект с3, пассивный с1
Обратите внимание на тип аргумента копирующего конструктора – это всегда ссылка на свой класс, ссылка на пассивный объект.
Работа копирующего конструктора при передаче объекта класса в качестве параметра функции (и возврате из функции объекта класса) будет рассмотрена позднее.
Друзья класса
Для того, чтобы предоставить легкий доступ к данным класса механизм с помощью ключа public не всегда удобен, ведь в этом случае public-переменные становятся общедоступны, то есть к ним может обратиться кто угодно из любой части программы. Механизм под названием «друзья класса» действует более гибко и позволяет предоставлять доступ избирательно. Например, можно предоставить доступ к закрытым переменным класса только для какой-либо функции или класса.
Друзья класса – это функции и классы, у которых есть полный доступ к классу, такой же, как у методов класса. Для получения прав друга, функция должна быть описана в классе со спецификацией friend.
Например, определим независимую функцию, которая вводит данные с клавиатуры в объект класса MyStr. Для этого в классе MyStr необходимо описать прототип дружественной функции:
Дружественная функция имеет два параметра , первый – ссылка на объект класса MyStr в который будет производиться ввод, второй - ограничитель ввода (максимальное число символов в строке). Описание дружественной функции размещаем в глобальной области программы.
#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
using namespace std;
class MyStr
{ static int count; // счетчик существующих строк
char *buf; // закрытые данные
// прототип дружественной функции
friend void inpstr(MyStr&, int );
public:
static int num; // счетчик порядкового номера
int nn; // порядковый номер строки
…
}; // конец определения класса
int MyStr::count=0;
int MyStr::num=0;
// определение дружественной функции
void inpstr(MyStr& s, int n)
{ char* fs; // указатель для доступа к дин.памяти
fs= new char[n+1]; // получение дин. памяти
cout<<"ввод с клавиатуры:"; // «приглашение» для ввода
cin>>fs;
strcpy(s.buf,fs); // копирование строки в закрытую область объекта класса
}
int main(int argc, char* argv[])
{MyStr c1("1_string"); // создание объекта с1
cout<<endl<<"Объект с1"<<endl;
c1.display();
MyStr c2(c1); // создание объекта с2 (инициализация копированием)
cout<<"Объект с2"<<endl;
c2.display();
MyStr c3=c1; // создание объекта с3 и присвоение ему значения с1
cout<<"Объект с3"<<endl;
c3.display();
cout<<"c1-"; // отображаем объект ввода
inpstr(c1,10); // вызов дружественной функции
cout<<"новый Объект с1"<<endl;
c1.display();
system ("pause");
return 0;
}
До сих пор мы решали задачи с использованием отдельных экземпляров класса, но для создания баз данных необходимы массивы объектов класса.
Создавая массивы объектов, например MyStr V[10], нам потребуется конструктор с инициализацией по-умолчанию. Такой конструктор может выглядеть, например так:
// конструктор по-умолчанию
MyStr()
{ num++; // при создании объекта счетчики наращиваются
count++;
nn=num;
// по-умолчанию выделяется строка под 60 символов
buf= new char[60];
buf="free"; // константная строка
}
Создадим массив из объектов по-умолчанию и распечатаем его в виде таблицы.
class MyStr
{…
// метод для вывода данных объекта в табличном виде
void display_tab()
{ cout<<nn<<'\t'<<buf<<endl;
}
…
}; // конец определения класса
int main(int argc, char* argv[])
{const int n=15;
int i;
// массив объектов, инициализируемых по-умолчанию
MyStr V[n];
// заголовок таблицы
cout<<" п/п \t строка"<<endl;
cout<<"------------------------"<<endl;
// цикл для вывода данных таблицы
for (i=0;i<n;i++) V[i].display_tab();
cout<<"Общее число записей = "<<V[0].get_count()<<endl;
system ("pause");
return 0;
}
