#include <iostream> // cout, endl
#include <iomanip> // setw, left
using namespace std;
class User{ // Класс User
string m_name; // Свойства класса имя
int m_age; // Свойства класса возраст
public:
User(string name, int age){ // Конструктор с параметрами name и age
m_name = name;
m_age = age;
cout << "name = " << setw(10) << left << name << "age = " << age << endl;
};
User(string name, char age) // Конструктор с прямой инициализацей (то же самое)
:m_name(name), m_age(age){}
};
int main(){
User Victor = User("Victor", 43); // Основная форма вызова конструктора
User Anna("Anna", 42); // Сокращенная форма
User * Ray = new User("Ray", 36); // Указатель на динамический объект
//В C++11 возможна списковая инициализация
User Alex = {"Alex", 46}; // C++11
User Dimon{"Dimon", 42}; // C++11
User Dimon_copy {Dimon}; // Создание копии объекта Dimon
User* Andrey = new User{"Andrey", 55}; // C++11 Ссылка на динамический объект
User R37[3]{{"Victor", 43}, {"Semen", 42}, {"Stas", 42}}; // Создание массива объектов с параметрическим конструктором
return 0;
}
/* Вывод программы
name = Victor age = 43
name = Anna age = 42
name = Ray age = 36
name = Alex age = 46
name = Dimon age = 42
name = Andrey age = 55
name = Victor age = 43
name = Semen age = 42
name = Stas age = 42
*/
- При использовании в классе указателей, после копирования или присваивания объектов может оказаться что они ссылаются на одни и те же данные. Для решения этой и других проблем, используют явные конструкторы копирования и перемещения и перегрузку оператора присваивания или перемещения. Рассмотрим возможные варианты:
- Конструктор копирования вызывается при создании объекта и операторе = на другой объект того же класса. T obj = obj1;
- Конструктор копирования вызывается при создании объекта и передачи ему в качестве аргумента (в круглых скобках) другого объекта того же класса по lvalue ссылке. T obj(T& obj1);
- Конструктор перемещения вызывается при создании объекта и операторе = на другой rvalue объект того же класса. T obj = std::move(T&& obj1);
- Конструктор перемещения вызывается при создании объекта и передачи ему в качестве аргумента (в круглых скобках) другого объекта того же класса по rvalue ссылке. T obj(std::move(T&& obj1));
- Оператор присваивания вызывается при уже созданном объекте и операторе = на другой объект того же класса. Реализация оператора присваивания - это перегрузка оператора =, которому передается аргумент(объект) по lvalue ссылке, если передавать аргумент (объект) по значению, то будет произведено предварительное копирование объекта - вызов конструктора копирования. obj = obj1;
- Оператор перемещения вызывается при уже созданном объекте и операторе = на другой rvalue объект того же класса. Реализация оператора перемещения - это перегрузка оператора =, которому передается аргумент(объект) по rvalue ссылке. obj = std::move(obj1);
- В конструкторе присваивания используют возврат из функции объекта класса, для того чтобы можно было каскадировать присваивания a = b= c;
#include <iostream>
class My{
public:
int id; // Идентификатор объекта
My(int id_m = 0){ // Конструктор
id = id_m; // Выполняем какие-то действия
std::cout << "Constructor id = " << id << "\n"; // Выводим сообщение
}
My(const My& obj){ // Конструктор копирования, принимает lvalue ссылку
std::cout << "Copy constructor\n"; // Выводим сообщение
id = obj.id; // Выполняем какие-то действия
}
My(const My&& obj){ // Конструктор перемещения принимает rvalue ссылку
std::cout << "Move constructor\n"; // Выводим сообщение
id = obj.id; // Выполняем какие-то действия
}
My& operator = (My& obj){ // Оператор присваивания принимает lvalue ссылку или объект
std::cout << "Operator=\n"; // Выводим сообщение
return obj; // Выполняем какие-то действия и возвращаем объект типа My
}
My& operator = (My&& obj){ // Оператор перемещения принимает rvalue ссылку
std::cout << "Move operator=\n"; // Выводим сообщение
return obj; // Выполняем какие-то действия и возвращаем объект типа My
}
};
int main() {
My s; //Constructor id = 0 Конструктор
My ss(3); //Constructor id = 3 Конструктор
My c = s; //Copy constructor Конструктор копирования
My cc(s); //Copy constructor Конструктор копирования
My m(std::move(s)); //Move constructor Конструктор перемещения, т.к. std::move() приводит объект к rvalue
My mm = std::move(ss); //Move constructor Конструктор перемещения,
c = s; //Operator= Оператор присваивания (объект c уже создан ранее)
c = std:: move(s); //Move operator= Оператор перемещения
ss = s = c; // Каскадирование операторов присваивания (будет дважды вызван конструктор копирования)
}
#include <iostream> // cout
using namespace std;
class Vector{ // Класс Vector
int size; // Размер массива
int *data; // Указатель на данные массива
public:
Vector(int s):size(s), data(new int[s]){cout << "Constructor \n";} // Конструктор
Vector(const Vector& v){ // Конструктор копирования, без него v3[0] = 9
cout << "Copy constructor \n";
size = v.size; // Копируем размер массива
data = new int[size]; // Создаем новый массив с размером как у источника
for(int i = 0; i < size; i++){ // Заполняем идентичными данными
data[i] = v.data[i];
}
}
Vector& operator = (Vector& v){ // Оператор присваивания (перегрузка оператора =)
cout << "Operator=";
size = v.size; // Копируем размер массива
data = new int[size]; // Создаем новый массив с размером как у источника
for(int i = 0; i < size; i++){ // Заполняем идентичными данными
data[i] = v.data[i];
}
return *this; // Возвращаем объект (не ссылку)
}
int& operator [](int index){ // Перегрузка оператора [] для обращения "как к массиву". int& className::operator [](int index){ должно быть в классе
cout << " \nOperator [] ";
return data[index]; // Возвращаем результат работы оператора (данные в массиве с индексом)
}
~Vector(){ // Деструктор
delete [] data; // Удаляем массив
}
};
int main(){
Vector v1(3); //Constructor Конструктор
Vector v2(3); //Constructor Конструктор
Vector v3(v1); //Copy constructor Конструктор копирования. v3 это независимая копия v1
v2 = v3; //Operator= Оператор присваивания. v2 это независимая копия v1
v1[0] = 9; //Operator [] Не меняет v2 и v3
v2[0] = 7; //Operator [] Не меняет v1 и v3
v3[0] = 5; //Operator [] Не меняет v1 и v2
cout << v1[0]; //Operator [] 9
cout << v2[0]; //Operator [] 7
cout << v3[0]; //Operator [] 5
}
/* Вывод в программы
Constructor
Constructor
Copy constructor
Operator=
Operator []
Operator []
Operator []
Operator [] 9
Operator [] 7
Operator [] 5
*/