Не могу поймать момент когда срабатывает перегруженный оператор присваивания, и плюс что можно оптимизировать

#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <Windows.h>
#include <map>
using namespace std;
class String {
private:
    unsigned int length;
    char* cstring;
  
    void clean() {
        if (cstring) {
            delete[] cstring;
        }
        length = 0;
    }
public:    
    String(const char* str) : length(strlen(str)),  cstring(new char[length + 1]) {
        strcpy(cstring, str);
        cout << "ctor" << endl;
    }
    String() : String("") {

    }
    String(int size, char symbol) : cstring(new char[size + 1]), length(size) {
        if (size) {
            memset(cstring, symbol, size);
            cstring[size] = '\0';
        }
    }
    String(const String& other) {
        length = other.length;
        cstring = new char[length + 1];
        strcpy(cstring, other.cstring);
    }
    String& operator=(const String& other) {
        cout << "String& operator=(const String& other)" << endl;
        if (this == &other)
            return *this;
        clean();
        length = other.length;
        cstring = new char[length + 1];
        strcpy(cstring, other.cstring);
        return *this;
    }
    String(String&& other) noexcept {
        length = other.length;
        cstring = other.cstring;      
        other.cstring = nullptr;
    }
    String& operator=(String&& other) noexcept {
        cout << "=&&" << endl;
        if (this == &other)
            return *this;
        
        clean();
        length = other.length;
        cstring = other.cstring;
        other.cstring = nullptr;
        return *this;
    }
    String& operator +=(String const& other) {
        cout << "=+" << endl;
        if (other.length) {
            unsigned int size = length + other.length;
            char* buff = new char[size + 1];
            strcpy(buff, cstring);
            strcat(buff, other.cstring);
            clean();
            cstring = buff;
            length = size;
            buff = nullptr;
            return *this;
        }
    }
    ~String() { 
        clean();
        cout << "dtor" << endl;
    }

    inline bool isEmpty() { return length == 0; }
    inline unsigned int getLength() { return length; }
    inline const char* c_str() const { return cstring; }
    bool operator==(const String& t) const {
        return length == t.length && strcmp(cstring, t.cstring) == 0;
    }
    bool operator !=(const String& t) const {
        return !(operator==(t));
    }
    char& operator[](const int i) {
        return cstring[i];
    }
    void stringLow() {
        for (int j = 0; j < length; ++j)
            cstring[j] = (char)(tolower(cstring[j]));
    }
    void stringUp() {
        for (int j = 0; j < length; ++j)
            cstring[j] = (char)(toupper(cstring[j]));
    }
    void reverse() {
        for (int i = 0; i < length / 2; i++) {
            swap(cstring[i], cstring[length - i - 1]);
        }
    }
    friend String operator+(String l, const String& r) {
        return l += r;
    }
    friend istream& operator >>(istream& is, String& obj) {
        char* buff = new char[99];
        memset(&buff[0], 0, sizeof(buff));
        is >> buff;
        obj = String(buff);
        delete[] buff;
        return is;
    }
    friend ostream& operator <<(ostream& os, const String& obj) {
        os << obj.cstring;
        return os;
    }
};

int main() {
    SetConsoleCP(1251);
    SetConsoleOutputCP(1251);
    String var("Люблю ");
    String var2("много пива");
    String var3 = var + var2;    
}

Ответы (1 шт):

Автор решения: Harry

Ну, я бы делал примерно так...

#include <cstring>
#include <iostream>

class String
{
private:
    size_t length;
    char* cstring;
  
    void swap(String& s)
    {
        std::swap(cstring,s.cstring);
        std::swap(length,s.length);
    }
public:    
    String(const char* str = "")
        : length(str ? strlen(str) : 0)
        , cstring(new char[length + 1])
    {
        strcpy(cstring, str ? str : "");
    }

    String(size_t size, char symbol)
        : length(size)
        , cstring(new char[size + 1])
    {
        memset(cstring, symbol, size);
        cstring[size] = '\0';
    }

    String(const String& other):String(other.cstring) {}

    String& operator=(const String& other)
    {
        if (this != &other)
        {
            String tmp(other);
            swap(tmp);
        }
        return *this;
    }

    String(String&& other) noexcept
        : length(other.length)
        , cstring(other.cstring)
    {
        other.length  = 0;
        // Вообще-то после move должно оставаться
        // СОГЛАСОВАННОЕ состояние, но у вас
        // нулевая длина означает пустую строку.
        // Возможно, стоит переписать эту строку
        // соответствующим образом:
        other.cstring = nullptr;
    }

    String& operator = (String&& other) noexcept
    {
        if (this != &other) swap(other);
        return *this;
    }

    String& operator +=(String const& other)
    {
        if (other.length) {
            size_t size = length + other.length;
            char* buff = new char[size + 1];
            strcpy(buff, cstring);
            strcat(buff, other.cstring);
            delete[] cstring;
            cstring = buff;
            length  = size;
        }
        return *this;
    }

    ~String()
    {
        delete[] cstring;
    }

    bool        isEmpty()   const { return length == 0; }
    size_t      getLength() const { return length; }
    const char* c_str()     const { return cstring; }

    bool operator==(const String& t) const noexcept
    {
        return length == t.length && strcmp(cstring, t.cstring) == 0;
    }
    bool operator !=(const String& t) const noexcept
    {
        return !(*this == t);
    }

    char& operator[](size_t i)  // Без проверок?
    {
        return cstring[i];
    }

    String& Lo() {
        for (int j = 0; j < length; ++j)
            cstring[j] = (char)(tolower(cstring[j]));
        return *this;
    }

    String& Up() {
        for (int j = 0; j < length; ++j)
            cstring[j] = (char)(toupper(cstring[j]));
        return *this;
    }

    String& reverse() {
        for (size_t i = 0, j = length-1; i < j; ++i, --j) {
            std::swap(cstring[i], cstring[j]);
        }
    }

    friend String operator+(const String& l, const String& r)
    {
        String t(l);
        return t += r;
    }

    friend std::istream& operator >>(std::istream& is, String& obj)
    {
        // Оставляю на вашей совести невозможность чтения длинных слов
        char* buff = new char[99];
        memset(&buff[0], 0, sizeof(buff));
        // Это в современном С++ не компилируемо:
        is >> buff;
        obj = String(buff);
        delete[] buff;
        return is;
    }

    friend std::ostream& operator <<(std::ostream& os, const String& obj) {
        os << obj.cstring;
        return os;
    }
};

using namespace std;

int main() {
    //SetConsoleCP(1251);
    //SetConsoleOutputCP(1251);
    String var("Люблю ");
    String var2("много пива");
    String var3;
    var3 = var + var2;   // Теперь тут есть присваивание
    cout << var << endl;
    cout << var2 << endl;
    cout << var3 << endl;
}

Основное - надо четко определиться с инвариантом класса. А то нулевая длина может соответствовать и пустой строке, и nullptr. Сама длина имеет смысл только при достаточно больших длинах строк, иначе она не очень-то что-то оптимизирует.

Тот же clean() не нужен, так как вызывается в ситуациях внутри класса, где обнуление длины не требуется. Да, и delete умеет работать с nullptr, проверка излишня.

Вобщем, посмотрите, что да как, если непонятно, зачем я что-то сделал не так - спрашивайте.

Да, на всякий случай: не поменяйте случайно порядок объявлений членов-данных, у вас на него конструктор опирается. И еще - при объявлении класса не стоит использовать using namespace, оставьте это пользователю. В классе же лучше использовать полностью квалифицированные имена.

→ Ссылка