
#include <iostream>

using namespace std;

class Str {
	char* s;
public:
	Str() : s(nullptr) {}

	Str(const Str& val): Str() {
		assign(val.s);
	}

	Str(Str&& val) {
		s = val.s;
		val.s = nullptr;
	}

	~Str() {
		clear();
	}

	void assign(const char* val)
	{
		clear();
		size_t len;
		for (len = 0; val[len]; ++len);
		s = new char[len + 1];
		size_t i = 0;
		for (i = 0; i <= len; i++)
			s[i] = val[i];
	}

	size_t length() const {
		size_t len;
		if(!s) return 0; //!!! added after the lab
		for (len = 0; s[len]; ++len);
		return len;
	}

	Str& operator =(const char* val) {
		assign(val);
		return *this;
	}

	Str& operator =(const Str& val) {
		assign(val.s);
		return *this;
	}

	Str& operator =(Str&& val) {
		swap(val.s, s);
		return *this;
	}

	Str operator +(const Str& val) const {
		//!!! I added the null checks here.
		if(!s && !val.s) return Str();
		char* ns = new char[length() + val.length() + 1];
		char* i = ns;
		if(s) for (char*si = s; *si; i++, si++) *i = *si;
		if(val.s) for (char*si = val.s; *si; i++, si++) *i = *si;
		*i = '\0'; // *i = 0;
		Str ret;
		ret.s = ns;
		return ret;
	}

	char& operator [](size_t idx) {
		return s[idx];
	}

	const char& operator [](size_t idx) const {
		return s[idx];
	}

	void clear()
	{
		if (s) delete s;
		s=nullptr; // <-- fixed
	}

	const char* c_str() const {
		return s;
	}
};

ostream& operator<< (ostream& o, const Str& val) {
	return o << val.c_str();
}

int main()
{
	Str a,b;
	a = b = "Ahoj";
	a = a + b;

	cout << a+b << endl;
	cout << (a + b)[6] << endl;
	cout << a[3] << endl;
}
