Бублик Володимир Васильович Об'єктно-орієнтоване програмування Частина 1. Об'єктне програмування. Лекція 2. Копіювання об'єктів Лекції для студентів 2 курсу Dresden, Zwinger
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 2 Повторення Що має бути в класі class T { private: //Тут розміщують атрибути public: //Конструктори T(T1,…,Tn); //Деструктор ~T();//далі селектори, модифікатори, … };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 3 Приклад класу. String class String { private: size_t _len; char* _allocator; public: String(); String(const char*); String(const char); ~String(); size_t length() const {return _len;} bool empty() const {return _len==0;} void clear() {*this=String();} };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 4 Констуктори Для чого у класі три різних конструктори? class String { private: size_t _len; char* _allocator; public: String(); String(const char*); String(const char); …………………………………………… };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 5 Властивість інкапсуляції Відокремлення реалізації класу від його визначення String::String (const char c): _allocator( new char [2]), _len(1) { _allocator [0]=c; _allocator [1]='\0'; return; }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 6 Питання Чому у визначенні класу розміщені реалізації? class String { private: size_t _len; char* _allocator; public:………………………………………….. size_t length() const {return _len;} bool empty() const {return _len==0;} void clear() {*this=String();} };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 7 Питання Чи коректний параметр замовчування? Ні. Чому? class String { private: size_t _len; char* _allocator; public: String (); String (const char* ps=0); String (const char); ~String (); };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 8 Конструктор копіювання class T { T(T1,…,Tn); ~T(); //конструктор копіювання //створює новий об'єкт, ідентичний //переданому параметром T(const T&); //Можливий варіант: T(T&); //але не Т(Т) };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 9 Використання Конструктор копіювання викликається кожного разу, коли параметр або результат передаються значеннями T1 f(T2 x) { T1 y; // тіло f… return y; } a=f(b);//T2 x(b); T1 y; тіло f… ; a = T1(y);
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 10 Інвентаризація об'єктів class Point { static int _freeID; const int _pointID; double _x; double _y; public: Point (double x=0, double y=0); Point (const Point &); ~Point(); };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 11 Конструктор Point Point::Point (double x, double y): _x (x), _y (y), pointID (++_freeID) { #ifdef NDEBUG cout<<pointID<<": created "<<*this<<endl; #endif return; }; //Де коректно розмістити замовчування параметру?
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 12 Копіювальний конструктор Point Point::Point (const Point & u): _x (u._x), _y (u._y), pointID(++_freeID) { #ifdef NDEBUG cout<<pointID<<": copied "<<*this<<endl; #endif return; }; //Чи може копіювання мати замовчуваний параметр?
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 13 Деструктор Point Point::~Point() { #ifdef NDEBUG cout<<pointID<<": removed "<<*this<<endl; #endif return; };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 14 Передача об'єктів параметрами Значенням Point operator+ (Point u, Point v) { Point res(u.x()+v.x(), u.y()+v.y()); return res; } Відсилками ostream& operator<<(ostream &os, const Point& u) { os<<'('<<u.x()<<','<<u.y()<<')'; return os; }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 15 Протокол int main() { Point a(1,2); Point b(5); a+b; return 0; } 1: created (1,2)//a 2: created (5,0)//b 3: copied (5,0)//v 4: copied (1,2)//u 5: created (6,2)//res 6: copied (6,2)//return 5: removed (6,2)//res 4: removed (1,2)//u 3: removed (5,0)//v 6: removed (6,2)//returned 2: removed (5,0)//b 1: removed (1,2)//a
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 16 Вправа до передачі об'єктів параметрами Що зміниться в протоколі, якщо у виводі забрати сталу відсилку? ostream& operator<<(ostream &os, Point u) { os<<'('<<u.x()<<','<<u.y()<<')'; return os; }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 17 Без локальної змінної Point operator+ (Point u, Point v) { /*Замість Point res(u.x()+v.x(), u.y()+v.y()); return res; */ return Point ( u.x()+v.x(), u.y()+v.y() ); }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 18 Протокол 2 int main() { Point a(1,2); Point b(5); a+b; return 0; } 1: created (1,2)//a 2: created (5,0)//b 3: copied (5,0)//v 4: copied (1,2)//u 5: created (6,2)//return 4: removed (1,2)//u 3: removed (5,0)//v 5: removed (6,2)//returned 2: removed (5,0)//b 1: removed (1,2)//a
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 19 Сталі відсилки Point operator+ (const Point & u, const Point & v) { return Point ( u.x()+v.x(), u.y()+v.y() ); }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 20 Протокол 3 int main() { Point a(1,2); Point b(1); a+b; return 0; } 1: created (1,2)//a 2: created (5,0)//b 3: created (6,2)//return 3: removed (6,2)//returned 2: removed (5,0)//b 1: removed (1,2)//a
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 21 Урок передачі параметрів Передаючи параметр і одержуючи результат, усвідомлюйте, з чим маєте справу: з оригіналом чи копією
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 22 Копіювання агрегатів class WrappedVector { private: static const size_t n; double * v; public: WrappedVector(); WrappedVector(const WrappedVector&); ~WrappedVector(); };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 23 Копіювальний конструктор вектора WrappedVector:: WrappedVector (const WrappedVector& vec): _v (new double[_n]) { for (size_t i=0; i<_n; i++) _v[i] = vec._v[i]; return; } //Як бути з нестачею пам'яті?
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 24 Копіювальний конструктор за замовчуванням WrappedVector:: WrappedVector (const WrappedVector& vec): _v (vec._v) { }; //Чим закінчиться виконання програми? int main() { WrappedVector u, v(u); return 64; //катастрофою!!! }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 25 Копіювання присвоєнням class WrappedVector { private: static const size_t n; double * v; public: WrappedVector(); WrappedVector(const WrappedVector&); ~WrappedVector(); WrappedVector& operator= (const WrappedVector&); };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 26 Реалізація копіювального присвоєння WrappedVector& WrappedVector::operator= (const WrappedVector& vec) { //Нам поталанило: vec і this мають одну й ту ж довжину for (size_t i=0; i<n; i++) v[i] = vec.v[i]; return *this; }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 27 Присвоєння за замовчуванням WrappedVector& WrappedVector :: operator= (const WrappedVector& vec) { v = vec; return *this; } //Чим закінчиться виконання програми? int main() { WrappedVector u, v; u=v; return 64;//знову катастрофою!!! }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 28 Вектори різної довжини class DissimilarVector { private: size_t _n;//non static, non const(?) double * _v; public: DissimilarVector(int); DissimilarVector(const DissimilarVector&); ~DissimilarVector(); DissimilarVector& operator=(const DissimilarVector&); };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 29 Конструктор вектора DissimilarVector:: DissimilarVector (size_t len) : _n (len), _v (new double[n]) { for (size_t i=0; i<_n; i++) _v[i] = 0; return; }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 30 Копіювальний конструктор DissimilarVector:: DissimilarVector (const DissimilarVector& vec): _n (vec._n), _v (new double[vec._n]) { if (_v==0) //throw exeption for (size_t i=0; i<_n; i++) _v[i] = vec._v[i]; return; }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 31 Чому атрибут _n не може бути сталим? 1.Спробуйте присвоєння за замовчуванням 2.Навіть копіювальне присвоєння, взагалі кажучи, не працюватиме
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 32 Копіювальне присвоєння DissimilarVector& DissimilarVector::operator= (const DissimilarVector& vec) { //1. Видалити старий об'єкт if (this==&vec) return *this; delete [] _v; //2. Створити новий об'єкт _n = vec._n; _v = new double[_n]; //3. Скопіювати значення for (size_t i=0; i<_n; i++) _v[i] = vec._v[i]; return *this; } _n = vec._n
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 33 Рядки з копіюванням class String { private: size_t _len; char* _allocator; public: String(); String(const char*); String(const char); String (const String & s); ~String(); };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 34 Копіювальний конструктор рядка String::String(String& s): _len( s._len), _allocator( new char[_len+1]) { strcpy(_allocator, s._allocator); return; };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 35 Редагування оригіналу (без const) class String { private: size_t _len; char* _allocator; int _amountOfCopies; public: String(); String(const char*); String(const char); String (String & s); ~String(); };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 36 Копіювання з редагуванням String::String(String& s): _len( s._len), _allocator( new char[_len+1]) { //Кількість копій, зроблених з оригіналу //збільшується на одиницю s._amountOfCopies++; strcpy(_allocator, s._allocator); return; };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 37 Мультиконструктор копіювання class String { public: String(); String(const char*); String(const char); String(const String & s, int multiplayer=1); ~String(); };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 38 Реалізація мультиконструктора копіювання String:: String(const String & s, int multiplayer): _len (s._len*multiplayer), _allocator (new char [_len+1]) { char * target = _allocator; for (int i=0; i<multiplayer; i++) { strcpy(target, s._allocator); target+=s._len; } return; };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 39 Застосування копіювання char * p …..; // Common constructor String s(p); // Copy version of multiplication constructor String ss(s); // Multiplication constructor String s10(s,10);
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 40 Проблема замовчуваного параметру Що станеться, якщо замовчуваний параметр перенести до реалізації? Катастофа class String { public: String(const String & s, int multiplayer); }; String:: String(const String & s, int multiplayer=1):…{…;} Чому?
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 41 Некоректне копіювання #include String.h // Common constructor String s(p); // Default copy constructor String ss(s); // Multiplication constructor String s10(s,10);
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 42 Сигнатури присвоєнь Якій з сигнатур віддати перевагу? 1.void operator=( T&); 2.T operator=( T&); 3.T& operator=( T ); 4.T operator=( T ); 5.T& operator=( T&);
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 43 Сигнатури присвоєнь Якій з сигнатур віддати перевагу? 1.void operator=( T&);//Як бути з x=y=z; 2.T operator=( T&);//чим копіювати результат? 3.T& operator=( T );//чим копіювати параметр? 4.T operator=( T );//див 2 і 3 разом 5.T& operator=( T&);//ОК!!!
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 44 Сигнатури присвоєнь Якій з сигнатур віддати перевагу? 1.void operator=( T&); 2.T operator=( T&); 3.T& operator=( T ); 4.T operator=( T ); 5.T& operator=( T&); T& operator=(const T&);
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 45 Що таке this? class T { public: T(T1,…,Tn); ~T(); T(const T&); T& operator= (const T&); }; this має тип T * const
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 46 Чому * const? this не можна перемістити на інший об'єкт this = anything; не коректно
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 47 Повернення значення в присвоєнні Point& Point::operator=(const Point & u) { this ->_x = u._x; *this._y = u._y; return *this; }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 48 Рядки з присвоєнням class String { public: String(); String(const char*); String(const char); String(const String & s, int multiplayer=1); String& operator=(const String&); ~String(); };
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 49 Присвоєння рядків String& String::operator=(const String& s) { if (this==&s) return *this; delete [] _allocator; _len = s._len; _allocator = new char[_len+1]; strcpy(_allocator, s._allocator); return *this; }
© Бублик В.В. ООП-1. Об'єктне програмування. Копіювання об'єктів 50 Висновки Конструктор копіювання створює новий об'єкт Присвоєння звичайно замінює існуючий об'єкт іншим об'єктом (навіть якщо не доводиться видаляти попередні значення) Присвоєння не можна визначити поза класом Присвоєння в класі T має тип T& (чому?) Присвоєння повертає *this, конструктори не повертають нічого (чому?)