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

Лекция 6. «Чёрные ящики» (ч. 4)

Лекция 6. «Чёрные ящики» (ч. 4)

ООП АФТИ 2015-2016, 2-й семестр

3749bacb748a9a39d77d007e87861559?s=128

Oleg Dashevskii

March 21, 2016
Tweet

Transcript

  1. ОБЪЕКТНО- ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ Лекция № 2 / 6
 21.03.2016 г.

  2. namespace Original { class String { public: String() {} //

    пустая строка ~String(); String(const String &); // копирование String(String &&other); // перемещение String &operator=(const String &); void append(char); // добавить 1 символ private: char *buf = 0; // буфер size_t len = 0; // длина буфера size_t used = 0; // количество символов }; }
  3. namespace Original { String::~String() { delete[] buf; } String::String(const String

    &other) : buf(new char[other.len]), len(other.len), used(other.used) { std::copy(other.buf, other.buf + used, buf); } String &String::operator=(const String &other) { delete[] buf; buf = new char[len = other.len]; used = other.used; std::copy(other.buf, other.buf + used, buf); return *this; } }
  4. namespace Original { String::String(String &&other) { buf = other.buf; other.buf

    = nullptr; len = other.len; used = other.used; other.len = other.used = 0; } String &String::operator=(String &&other) { delete[] buf; buf = other.buf; other.buf = nullptr; len = other.len; used = other.used; other.len = other.used = 0; return *this; } }
  5. namespace Original { void String::reserve(size_t n) { if (len <

    n) { size_t newlen = std::max(len + len/2, n); char *newbuf = new char[newlen]; std::copy(buf, buf + used, newbuf); delete[] buf; buf = newbuf; len = newlen; } } void String::append(char c) { reserve(used + 1); buf[used++] = c; } }
  6. namespace Optimized { struct StringBuf { StringBuf() {} ~StringBuf(); void

    reserve(size_t n); // обеспечить len >= n char* buf = nullptr; // буфер size_t len = 0; // длина буфера size_t used = 0; // количество символов unsigned refs = 1; // количество ссылок }; class String { public: String(); // пустая строка ~String(); // уменьшить счетчик ссылок // (удалить буфер если refs==0) String(const String &); // увеличить счетчик ссылок String(String &&); // переместить ссылку void append(char); // добавить символ String &operator=(const String &); String &operator=(String &&); private: void about_to_modify(size_t n); StringBuf *data = nullptr; }; } Copy-on-write (COW)
  7. namespace Optimized { struct StringBuf { StringBuf() {} ~StringBuf(); void

    reserve(size_t n); // обеспечить len >= n StringBuf(const StringBuf &) = delete; StringBuf(StringBuf &&) = delete; StringBuf &operator=(const StringBuf &) = delete; StringBuf &operator=(StringBuf &&) = delete; char* buf = nullptr; // буфер size_t len = 0; // длина буфера size_t used = 0; // количество символов unsigned refs = 1; // количество ссылок }; } Запрещаем нежелательные операции
  8. namespace Optimized { StringBuf::~StringBuf() { delete[] buf; } void StringBuf::reserve(size_t

    n) { if (len < n) { size_t newlen = std::max(len + len/2, n); char *newbuf = new char[newlen]; std::copy(buf, buf + used, newbuf); delete[] buf; buf = newbuf; len = newlen; } } } Реализация StringBuf
  9. namespace Optimized { String::String() : data(new StringBuf) {} String::~String() {

    if (data && --data->refs < 1) delete data; } String::String(const String &other) : data(other.data) { ++data->refs; } String &String::operator=(const String &other) { if (data && --data->refs < 1) delete data; ++(data = other.data)->refs; return *this; } }
  10. namespace Optimized { String::String(String &&other) : data(other.data) { other.data =

    nullptr; } String &String::operator=(String &&other) { if (data && --data->refs < 1) delete data; data = other.data; other.data = nullptr; return *this; } }
  11. namespace Optimized { void String::about_to_modify(size_t n) { if (data->refs >

    1) { std::unique_ptr<StringBuf> newdata(new StringBuf); newdata->reserve(std::max(data->len, n)); std::copy(data->buf, data->buf + data->used, newdata->buf); newdata->used = data->used; --data->refs; data = newdata.release(); } else data->reserve(n); } void String::append(char c) { if (!data) return; about_to_modify(data->used + 1); data->buf[data->used++] = c; } }
  12. ЗАДАЧА: ЕЩЕ 2 МЕТОДА namespace Optimized { class String {

    // ... size_t length() const; // длина строки char &operator[](size_t); // доступ по индексу }; }
  13. namespace Optimized { size_t String::length() const { return data ?

    data->used : 0; } }
  14. char &String::operator[](size_t n) { return *(data->buf + n); } Первая

    попытка void f(Optimized::String &s) { Optimized::String s2(s); // копия s[0] = 'x'; // ОЙ! s2 тоже меняется! } FAIL!
  15. char& String::operator[](size_t n) { about_to_modify(data->len); return *(data->buf + n); }

    Вторая попытка void f(Optimized::String &s) { char &rc = s[0]; // ссылка на 1-й символ Optimized::String s2(s); // копия rc = 'x'; // ОЙ! s2 тоже меняется! } FAIL!
  16. РЕШЕНИЕ ПРОБЛЕМЫ • «Неразделяемый» StringBuf. • Взятие ссылки не только

    копирует буфер, но и помечает его неразделяемым. • Пометка действует до первой операции, изменяющей строку (после нее ссылки все равно можно считать недействительными).
  17. constexpr UNSHAREABLE = numeric_limits<unsigned>::max(); StringBuf::StringBuf(const StringBuf &other, size_t n) {

    reserve(std::max(other.len, n)); std::copy(other.buf, other.buf + other.used, buf); used = other.used; } void String::about_to_modify(size_t n, bool unshareable /* = false */) { if (data->refs > 1 && data->refs != UNSHAREABLE) { StringBuf *newdata = new StringBuf(*data, n); --data->refs; data = newdata; } else data->reserve(n); data->refs = unshareable ? UNSHAREABLE : 1; } char &String::operator[](size_t n) { about_to_modify(data->len, true); return *(data->buf+n); }
  18. String::String(const String &other) { if (other.data->refs != UNSHAREABLE) ++(data =

    other.data)->refs; else data = new StringBuf(*other.data); } String::~String() { if (data->refs == UNSHAREABLE || --data->refs < 1) delete data; } String &String::operator=(const String &other) { if (data->refs == UNSHAREABLE || --data->refs < 1) delete data; if (other.data->refs != Unshareable) ++(data = other.data)->refs; else data = new StringBuf(*other.data); return *this; }
  19. String::String(String &&other) { if (other.data->refs != UNSHAREABLE) { data =

    other.data; other.data = nullptr; } else data = new StringBuf(*other.data, 0); } String &String::operator=(String &&other) { if (data->refs == UNSHAREABLE || --data->refs < 1) delete data; if (other.data->refs != UNSHAREABLE) { data = other.data; other.data = nullptr; } else data = new StringBuf(*other.data, 0); return *this; }
  20. «БОЛЕВЫЕ ТОЧКИ» • Конструкторы копирования и перемещения. • Операторы присваивания.

    • Инициализация указателей в конструкторе и не только (лечится с помощью smart pointer). • Неявное преобразование типов (лечится с помощью explicit). • Корректная реализация перегрузки операторов.