#include <cstring>
#include <iostream>
#include <vector>

int alloc_count{0};

char *logging_alloc(size_t n) {
    ++alloc_count;
    std::cerr << "Allocating " << n << " bytes" << std::endl;
    return new char[n];
}

class string {
    char *m_str; // In modern C++, should use std::unique_ptr
                 // But doing manual memory management for explanation's sake
    size_t m_len;
    // No capacity for dead simplicity

    // For internal use, for + operator
    string(char *str, size_t len) :
        m_str(str), m_len(len) { }
public:
    // Accessor
    char *c_str() {
        return m_str;
    }

    // Initialize from C string
    string(const char *str) {
        m_len = strlen(str);
        m_str = logging_alloc(m_len + 1);
        strcpy(m_str, str);
    }

    // Default constructor
    string() {
        m_str = nullptr;
        m_len = 0;
    }

    // Copy constructor
    string(const string &other) {
        m_len = other.m_len;
        m_str = logging_alloc(m_len + 1);
        strcpy(m_str, other.m_str);
    }

    // Assignment: Uses copy (or move) constructor to construct argument
    // Uses destructor to free own allocation. This is a trick to avoid
    // having to implement two assignment operators for move and copy
    // assignment in addition to two constructors for moving and copying.
    // Many types, for various reason, still define the two types of
    // assignment separately.
    string &operator=(string other) {
        std::swap(m_len, other.m_len);
        std::swap(m_str, other.m_str);
        return *this;
    }

#ifdef MOVE
    // Move constructor
    string(string &&other) noexcept {
        m_len = other.m_len;
        m_str = other.m_str;
        other.m_str = nullptr;
    }
#endif

    friend string operator+(const string &a, const string &b) {
        size_t len = a.m_len + b.m_len + 1;
        char *str = logging_alloc(len);
        strcpy(str, a.m_str);
        strcat(str, b.m_str);
        return string(str, len);
    }

    string &operator+=(const string &other) {
        return *this = *this + other;
    }

    ~string() {
        delete [] m_str;
    }
};

int main(int argc, char **argv) {
    std::vector<string> v;
    for (int i = 0; i < argc; ++i) {
        v.push_back(string(argv[i])); // copy in C++03, move in C++11
    }

#ifdef OUTPUT_STRINGS
    string acc("");
    for (auto &str: v) {
        acc += str;
        acc += string(" ");
    }
    std::cout << acc.c_str() << std::endl;
#endif

    std::cout << "Total allocations: " << alloc_count << std::endl;
    return 0;
}
