Не могу поймать момент когда срабатывает перегруженный оператор присваивания, и плюс что можно оптимизировать
#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 шт):
Ну, я бы делал примерно так...
#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, оставьте это пользователю. В классе же лучше использовать полностью квалифицированные имена.