#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:
С вашим кодом много проблем.
- Вы управляете своей собственной памятью. Вам следует избегать этого, если это вообще возможно.
- Вы постоянно забываете, что строки имеют нулевой завершитель. Для размещения strin
Hello, world!
вам нужен буфер символов размером 14 байт, а не 13. - У вас есть
len
переменная-член, которая выполняет фактически то же самое, что иstrlen
функция, за исключением непоследовательного рассмотрения пункта 1 выше. - В вашем классе 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, который никогда не используется внутри.