Нечетный вывод из программы при вводе оператора?

#c

#c

Вопрос:

Вероятно, я здесь упускаю что-то очевидное. Это мой код (я только изучаю настоящий C и хочу немного попрактиковаться):

 #include <iostream>
#include <cstring>

using namespace std;

class String {
private:
    char * value;
    int len;
    friend ostream amp; operator<<(ostream amp; os, String s);
public:
    String();
    String(const char * base);
    ~String();
    String operator (String s);
    String operator*(int n);
    int length();
};

String::String() {
    this->value = new char[0];
    this->len = 0;
}

String::String(const char * base) {
    this->value = new char[this->len = strlen(base)];
    strcpy(this->value, base);
}

String::~String() {
    delete [] this->value;
}

int String::length() {
    return this->len;
}

String String::operator (String s) {
    String n;
    delete [] n.value;

    cout << "Upon entering, I am: "" << *this << ""n";

    n.value = new char[this->len   s.len];
    for(int i = 0; i < this->len; i  ) {
        n.value[i] = this->value[i];
    }
    for(int i = 0; i < s.len; i  ) {
        n.value[i   this->len] = s.value[i];
    }

    n.len = this->len   s.len;

    cout << "String::operator (" << *this << ", " << s << ") succeeded with new value = "" << n << ""n";

    return n;
}

String String::operator*(int n) {
    String s;
    delete [] s.value;

    s.value = new char[this->len * n];

    for(int i = 0; i < this->len * n; i  ) {
        s.value[i] = this->value[i % this->len];
    }

    cout << "String::operator* succeeded with new value = "" << s << ""n";

    return s;
}

ostream amp; operator<<(ostream amp; os, String s) {
    return os << s.value;
}

int main() {
    String s("Hello, world!");
    cout << s << "nLength = " << s.length() << "nn";
    cout << (s   String("n")) * 5;
    return 0;
}
  

И строка инициализируется и отображается правильно, но мой вывод действительно странный; кажется, что при вводе operator «Привет, мир!» внезапно становится «x% r»?

 C:UsersRyanDocumentsMy DropboxC   Projects>strings
Hello, world!
Length = 13

Upon entering, I am: "x%r"
String::operator (x%r,
) succeeded with new value = "x%r"
String::operator* succeeded with new value = "╚%r"
─
  

Комментарии:

1. Re: this->value = new char[this->len = strlen(base)]; Буфер недостаточно велик, чтобы вместить завершение .

2. @John Dibling: Спасибо, изменено.

3. Проверьте так называемое «Правило трех» ( en.wikipedia.org/wiki/Rule_of_three_(C++_programming) ) тем более, что ваш класс управляет указателями.

4. Подождите, нет, я использую переменную len для определения длины, поэтому окончание не обязательно, верно?

5. @Eugen: Хорошо, я определил конструктор копирования, но нужен ли мне оператор присваивания копирования? Я могу просто перезаписать строку, поскольку она неизменяема, верно?

Ответ №1:

Попробуйте это:

 ostream amp; operator<<(ostream amp; os, const Stringamp; s) {
    return os << s.value;
}
  

в противном случае вы должны определить конструктор копирования для вашего класса String.

Комментарии:

1. Вау, это сработало! Можете ли вы сказать мне, почему? Указатели скопированы неправильно или что-то в этом роде?

2. @minitech: Без вашего собственного конструктора копирования компилятор предоставит свой собственный, который просто копирует элементы такими, какие они есть. В этом случае вы получаете ситуацию, когда две ваши строки используют одно и то же ->value . И если один экземпляр будет уничтожен, у другого будет значение, указывающее на этот удаленный фрагмент памяти. const Stringamp; s сообщает компилятору использовать s по ссылке — без ее копирования.

Ответ №2:

Вам необходимо предоставить конструктор копирования и оператор присваивания.

Ответ №3:

С вашим кодом много проблем.

  1. Вы управляете своей собственной памятью. Вам следует избегать этого, если это вообще возможно.
  2. Вы постоянно забываете, что строки имеют нулевой завершитель. Для размещения strin Hello, world! вам нужен буфер символов размером 14 байт, а не 13.
  3. У вас есть len переменная-член, которая выполняет фактически то же самое, что и strlen функция, за исключением непоследовательного рассмотрения пункта 1 выше.
  4. В вашем классе string нет конструктора копирования, что приводит к появлению произвольных указателей и, в конечном итоге, к сбою.

Вот рефакторинг вашего кода, который в значительной степени работает.

 #define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstring>

using namespace std;

class String {
private:
    char * value;
//    size_t len;
    friend ostream amp; operator<<(ostream amp; os, String s);
public:
    String();
    String(const char * base);
    String(const Stringamp; rhs) 
    {
        value = new char[strlen(rhs.value) 1];
        strcpy(value,rhs.value);
    }
    ~String();
    String operator (String s);
    String operator*(int n);
    size_t length();
};

String::String() {
    this->value = new char[0];
}

String::String(const char * base) {
    this->value = new char[strlen(base) 1];
    strcpy(this->value, base);
}

String::~String() {
    delete [] this->value;
}

size_t String::length() {
    return strlen(value);
}

String String::operator (String s) {
    String n;
    delete [] n.value;

    cout << "Upon entering, I am: "" << *this << ""n";

    n.value = new char[strlen(value) strlen(s.value) 1];
    for(int i = 0; i < strlen(value); i  ) {
        n.value[i] = this->value[i];
    }
    for(int i = 0; i < strlen(s.value); i  ) {
        n.value[i   strlen(value)] = s.value[i];
    }
    n.value[strlen(value) strlen(s.value)] = '';

    cout << "String::operator (" << *this << ", " << s << ") succeeded with new value = "" << n << ""n";

    return n;
}

String String::operator*(int n) {
    String s;
    delete [] s.value;

    s.value = new char[(strlen(value)*n) 1];

    for(int i = 0; i < strlen(value) * n; i  ) {
        s.value[i] = this->value[i % strlen(value)];
    }
    s.value[strlen(value)*n] = '';

    cout << "String::operator* succeeded with new value = "" << s << ""n";

    return s;
}

ostream amp; operator<<(ostream amp; os, String s) {
    return os << s.value;
}

int main() {
    String s("Hello, world!");
    cout << s << "nLength = " << s.length() << "nn";
    cout << (s   String("n")) * 5;
    return 0;
}
  

Комментарии:

1. Как упоминалось в комментариях — терминатор не нужен из-за len , конструктор копирования уже удален, strlen нежелательный. Конечно, я бы не стал управлять своей собственной памятью в реальном коде, но это практика.

2. В терминаторе » нет необходимости. strlen и strcpy без этого не будет работать.

3. … и я не использую ни strlen , ни strcpy за исключением в конструкторе C string, который никогда не используется внутри.