Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Лекция № 2

Oleg Dashevskii
September 26, 2017

Лекция № 2

ООП АФТИ ФФ НГУ 2017, осенний семестр

Oleg Dashevskii

September 26, 2017
Tweet

More Decks by Oleg Dashevskii

Other Decks in Education

Transcript

  1. struct Point { double x, y, z; Point() = default;

    Point(double _x, double _y, double _z) : x(_x), y(_y), z(_z) {} }; Point a(10, 20, 30); Point b(a); Point c(Point(1, 2, 3)); Point d; Конструктор, определенный 
 пользователем Конструктор копирования
 (неявный) Конструктор по умолчанию Конструктор перемещения
 (неявный)
  2. КОНСТРУКТОР КОПИРОВАНИЯ Point zeroPoint() { return Point(0, 0, 0); }

    1. Объект является возвращаемым значением
  3. ВАРИАНТЫ X(const X& copy_from_me); X(X& copy_from_me); X(volatile X& copy_from_me); X(const

    volatile X& copy_from_me); X(X& copy_from_me, int = 0); X(const X& copy_from_me, double = 1.0, int = 42);
  4. struct Point { double x, y, z; Point(double _x, double

    _y, double _z) : x(_x), y(_y), z(_z) {} // Не надо такое писать! Point(const Point &other) : x(other.x), y(other.y), z(other.z) {} }; Такой конструктор можно не писать, 
 он будет сгенерирован автоматически 
 (копирование всех полей)
  5. struct Buffer { char *buf; size_t size; Buffer(size_t sz) :

    buf(new char[sz]), size(sz) {} ~Buffer() { delete buf; } }; Как здесь будет работать КК по умолчанию?
  6. Buffer::Buffer(const Buffer &other) : size(other.size) { buf = new char[size];

    if (buf) memcpy(buf, other.buf, size); } Иначе: будет очень-очень плохо!
  7. Buffer fillBuffer() { Buffer b(1024); memset(b.buf, 0, b.size); return b;

    } Buffer mybuf = fillBuffer(); Насколько хорошо и быстро будет
 это работать?
  8. 1. Конструктор A() внутри fillBuffer(). 2. Конструктор копирования во временное

    значение при return. 3. Конструктор копирования в mybuf. В наихудшем случае:
  9. КОНСТРУКТОР ПЕРЕМЕЩЕНИЯ struct Buffer { char *buf; size_t size; Buffer(size_t

    sz); Buffer(const Buffer &other); Buffer(Buffer &&other) { buf = other.buf; size = other.size; other.buf = nullptr; other.size = 0; } ~Buffer(); };
  10. • Foo & – обычная lvalue-ссылка. Можно изменять объект, на

    который она ссылается. • Foo const & – константная lvalue-ссылка. Объект изменять нельзя. • Foo && – rvalue-ссылка. Ссылка на временный объект, который можно изменять, поскольку он всё равно больше использоваться не будет. «Деструктивное чтение».
  11. // Классический swap template<class T> void swap(T& a, T& b)

    { T tmp {a}; // две копии a a = b; // две копии b b = tmp; // две копии tmp (старое a) }
  12. // Почти идеальный swap template<class T> void swap(T& a, T&

    b) { T tmp {static_cast<T&&>(a)}; // конструктор может затереть a a = static_cast<T&&>(b); // присваивание может затереть b b = static_cast<T&&>(tmp); // присваивание может затереть tmp } // То же самое, с использованием std::move template<class T> void swap(T& a, T& b) { T tmp {std::move(a)}; // перемещаем из a a = std::move(b); // перемещаем из b b = std::move(tmp); // перемещаем из tmp }
  13. ПЕРЕГРУЗКА ОПЕРАТОРА ПРИСВАИВАНИЯ • Существует реализация по умолчанию: копирование всех

    полей. • Если в классе присутствуют указатели, реализация по умолчанию приносит проблемы.
  14. struct Buffer { char *buf; size_t size; Buffer(size_t sz) :

    buf(new char[sz]), size(sz) {} ~Buffer() { delete buf; } }; Buffer b1(100), b2(200); b1 = b2; // ОЙ
  15. Buffer &Buffer::operator=(const Buffer &other) { delete buf; buf = new

    char[size = other.size]; if (buf) memcpy(buf, other.buf, size); return *this; } Buffer buf(100), buf2(200); buf = buf; // ОЙ
  16. Buffer &Buffer::operator=(const Buffer &other) { if (this != &other) {

    delete buf; buf = new char[size = other.size]; if (buf) memcpy(buf, other.buf, size); } return *this; } Защита от самоприсваивания
  17. Buffer &Buffer::operator=(Buffer &&other) { buf = other.buf; buf.size = other.size;

    other.buf = nullptr; other.size = 0; return *this; } Присваивание с перемещением
  18. ОПЕРАЦИИ ПО УМОЛЧАНИЮ • X() • X(const X &) •

    X& operator=(const X &) • X(X &&) • X& operator=(X &&) • ~X()
  19. struct S { string a; int b; }; S f(S

    arg) { S s0 {}; // конструктор по умолчанию: {"",0} S s1 {s0}; // конструктор копирования s1 = arg; // копирующее присваивание return s1; // конструктор перемещения }
  20. • Если явно определён любой конструктор, конструктор по умолчанию X()

    не генерируется. • Если явно объявлена любая операция копирования, перемещения или деструктор, то операции копирования, перемещения и деструктор не генерируются. Но:
  21. ИНИЦИАЛИЗАЦИЯ
 C++11 STYLE X a1 {v}; // новое в C++11

    X a2 = {v}; // было ещё в C X a3 = v; // было ещё в C X a4(v); // «старый» C++
  22. ПРЕИМУЩЕСТВА {} • Нет «обрезания» типов. char можно присвоить int,

    но не наоборот. Аналогично с double и float. • Нет преобразования между целочисленными типами и типами с плавающей точкой.
  23. void f(double val, int val2) { int x2 = val;

    // если val==7.9, x2 <- 7 char c2 = val2; // если val2==1025, c2 <- 1 int x3 {val}; // ОШИБКА char c3 {val2}; // ОШИБКА char c4 {24}; // OK. 24 входит в char char c5 {264}; // ОШИБКА. 264 не входит в 8-битный char int x4 {2.0}; // ОШИБКА. int и double. // ... }
  24. int x4 {}; // 0 double d4 {}; // 0.0

    char *p {}; // nullptr vector<int> v4{}; // пустой вектор string s4 {}; // пустая строка char buf[1024] {}; // все символы 0 {} — значение по умолчанию
  25. struct Work { string author; string name; int year; };

    // Инициализация почленно Work s9 { "Beethoven", "Symphony No. 9 in D minor, Op. 125; Choral", 1824 }; // Конструктор копирования по умолчанию Work currently_playing { s9 }; Work none {}; // аналог: { {}, {}, {} } или { "", "", 0 } Инициализация
 объектов классов без конструкторов
  26. struct X { X(int); }; X x0; // ОШИБКА. Нет

    инициализации X x1 {}; // ОШИБКА. Пустая инициализация X x2 {2}; // ОК X x3 {"two"}; // ОШИБКА. Не тот тип. X x4 {1,2}; // ОШИБКА. Неверное количество X x5 {x4}; // OK. Конструктор копирования по умолч. Инициализация с конструктором
  27. struct S1 { int a, b; }; struct S2 {

    int a, b; S2(int aa = 0, int bb = 0) : a(aa), b(bb) {} }; S1 x11(1,2); // ОШИБКА. Нет конструктора S1 x12 {1,2}; // OK. Почленная инициализация S1 x13(1); // ОШИБКА. Нет конструктора S1 x14 {1}; // OK. x14.b <- 0 S2 x21(1,2); // ОК. Конструктор S2 x22 {1,2}; // ОК. Конструктор S2 x23(1); // OK. Конструктор с арг. по умолч. S2 x24 {1}; // OK. Конструктор с арг. по умолч.
  28. vector<double> v { 1, 2, 3.456, 99.99 }; list<pair<string,string>> languages

    { { "Nygaard", "Simula" }, { "Richards", "BCPL" }, { "Ritchie", "C"} }; map<vector<string>,vector<int>> years { { { "Maurice", "Vincent", "Wilkes" }, { 1913, 1945, 1951, 1967, 2000 } }, { { "Martin", "Richards"}, { 1982, 2003, 2007 } }, { { "David", "John", "Wheeler"}, { 1927, 1947, 1951, 2004 } } }; // НО КАК?!! Произвольное количество
 аргументов конструктора
  29. #include <initializer_list> #include <iostream> void f(std::initializer_list<int> list) { std::cout <<

    "Size = " << list.size() << std::endl; for (int x: list) std::cout << x << std::endl; } f({1, 2}); f({23, 345, 4567, 56789}); f({}); // Пустой список std::initializer_list<T>
  30. • Если можно вызвать конструктор по умолчанию или конструктор со

    списком, используется первый. • Если можно вызвать конструктор со списком или «обычный» конструктор, также используется первый. struct X { X(initializer_list<int>); X(); X(int); }; X x0 {}; // X() X x1 {1}; // X(initializer_list<int>)
  31. vector<int> v1 {1}; // Вектор из одного элемента: 1 vector<int>

    v2 {1,2}; // Вектор из двух элементов: 1,2 vector<int> v3(1); // Вектор из одного элемента: 0 (по умолч.) vector<int> v4(1,2); // Один элемент со значением 2 Разница между () и {}
  32. • Можно перегрузить поведение почти всех операторов, существующих в языке.

    • Нельзя создать новые операторы. • Нельзя повлиять на приоритет, арность или ассоциативность операторов.
  33. A a; SomeType b; // a + b class A

    { // ... return_type operator+(SomeType b); }; // b + a ? Оператор-член класса
  34. Оператор-глобальная функция class A { // ... friend return_type operator+(const

    A &, SomeType); friend return_type operator+(SomeType, const A &); }; inline return_type operator+(const A &a, SomeType b) { // ... } inline return_type operator+(SomeType b, const A &a) { // ... }
  35. АРИФМЕТИКА • + - * / % • Можно использовать

    оба варианта функций. • operator- бывает двух видов!
  36. ПОБИТОВЫЕ ОПЕРАЦИИ • ^ | & ~ << >> •

    Приоритет ниже, чем у арифметики:
 a ^ n + b воспримется как a ^ (n + b). • Можно использовать оба варианта функций. • << и >> используются стандартной библиотекой для ввода/вывода.
  37. ostream& operator<<(ostream& out, const Vector2D& vec) { // вывод out

    << "(" << vec.x() << ", " << vec.y() << ")"; return out; } istream& operator>>(istream& in, Vector2D& vec) { // ввод double x, y; // пропускаем открывающую скобку in.ignore(1); // читаем x in >> x; vec.set_x(x); // пропускаем разделитель in.ignore(2); // читаем y in >> y; vec.set_y(y); // пропускаем закрывающую скобку in.ignore(1); return in; }
  38. ОПЕРАТОР ПРИСВАИВАНИЯ • Только метод класса! • Существует реализация по

    умолчанию: копирование всех полей. • Если в классе присутствуют указатели, реализация по умолчанию приносит проблемы (см. предыдущая лекция).
  39. ОПЕРАТОРЫ СРАВНЕНИЯ • == != < <= > >= •

    Можно использовать оба варианта функций. • Чтобы не мучаться: • #include <utility> • using namespace std::rel_ops; • Определить operator== и operator< • Остальные операторы обеспечит STL
  40. ЛОГИЧЕСКИЕ ОПЕРАТОРЫ • ! && || • При задании operator&&

    теряется свойство «короткого замыкания».
  41. bool Function1() { return false; } bool Function2(); Function1() &&

    Function2(); ////////////////////////////////// MyBool Function3() { return MyBool::FALSE; } MyBool Function4(); bool operator&&(MyBool const &, MyBool const &); Function3() && Function4(); Function2() не будет вызвана, а Function4() – будет!
  42. ПРИСВОЕНИЕ- МОДИФИКАЦИЯ • += -= *= /= %= &= |=

    ^= <<= >>= • Метод класса! • operator+= не генерируется автоматически из operator= и operator+. • Нужно возвращать *this.
  43. ОПЕРАТОРЫ ИНКРЕМЕНТА И ДЕКРЕМЕНТА SomeValue& SomeValue::operator++() // префиксный { ++data;

    return *this; } SomeValue SomeValue::operator++(int unused) // постфиксный { SomeValue result = *this; ++data; return result; } SomeValue v; ++v; // префиксный ++ v++; // постфиксный ++
  44. // постфиксный SomeValue SomeValue::operator++(int unused) { SomeValue result = *this;

    ++(*this); // вызов SomeValue::operator++() return result; } Реализация постфиксного оператора 
 через префиксный
  45. • Должен быть членом класса. • Допустим один аргумент, зато

    любого типа. • Обычно переопределяется две версии: c const и без. ОПЕРАТОР ИНДЕКСА
  46. template <typename T> class StupidVector { // 100 элементов должно

    хватить всем! T array[100]; public: // ... T &operator[](size_t idx) { return array[idx]; } const T &operator[](size_t idx) const { return array[idx]; } };
  47. template <typename T> class Matrix; template <typename T> class MatrixColumn

    { public: MatrixColumn(Matrix *m, size_t r) : matrix(m), row(r) {} T &operator[](int col) { return matrix->element(row, col); } Matrix *matrix; size_t row; }; template <typename T> class Matrix { public: Matrix(int rows, int cols); // ... T &element(int row, int col); MatrixColumn<T> operator[](int row) { return MatrixColumn<T>(this, row); } }; Matrix<double> mat(3, 3); mat[2][2] = 10;