Member 11732139 Ответов: 2

Почему указатель char выделяется с помощью new


Привет, я построил свой собственный класс string, где я использую char* str
ниже приведен фрагмент кода

Снизу фрагмент кода, в параметров и конструктор копирования
ул. КСТР = ; //эта часть заявления работает нормально.

но даже тогда, почему мне нужно выделять память с помощью new, а затем strcpy требуется?
cstr = новый символ[strlen (str) +1];
strcpy(cstr, str);

обе работы хорошо. но зачем выбирать метод динамического распределения?

Что я уже пробовал:

class CString
{
private:
	char* cstr;
public:
	CString();
	CString(char* str);
	CString(CString& str);
	~CString();

	operator char*();
	//operator const char*();
	CString operator+(const CString& q)const;
	CString operator=(const CString& q);
};
CString::CString()
{
	cout << "constructor" << endl;
	cstr = 0;
}
CString::CString(char *str)
{
	cout << " param constructor" << endl;
	//cstr = str;// this works fine even then we allocating with new why???
	cstr = new char[strlen(str) +1];
	strcpy(cstr, str);
}
CString::CString(CString& q)
{
	cout << " copy constructor" << endl;
	if (this == &q)
		return;
	cstr = q.cstr;
	//cstr = new char[strlen(q.cstr) + 1];
	//strcpy(cstr, q.cstr);
}
CString::~CString()
{
	//if (cstr)
	//	delete[] cstr;
}
CString::operator char*()
{
	cout << " operatorcahr*" << endl;
	return cstr;
}
//CString::operator const char* ()
//{
//	cout << " const operatorcahr*" << endl;
//	return cstr;
//}
CString CString::operator +(const CString &q) const
{
	cout << "operator +*" << endl;
	CString s;
	s.cstr = new char[strlen(cstr) + strlen(q.cstr) + 1];
	strcpy(s.cstr, cstr);
	strcat(s.cstr, q.cstr);
	return s;
}
CString CString::operator =(const CString &q)
{
	cout << "operator =" << endl;
	if (this != &q)
	{
		//if (cstr)
			//delete[] cstr;
		cstr = new char[strlen(q.cstr) + 1];
		strcpy(cstr, q.cstr);
	}
	return *this;
}

int _tmain(int argc, _TCHAR* argv[])
{
	CString a = CString("Hello") + CString(" World");
	cout << a << endl;
	getchar();
	return 0;
}

Philippe Mori

Хотя написание класса string-это хорошее упражнение, обычно следует использовать стандартный класс string в производственном коде, поскольку стандартный класс был закодирован экспертами, которые лучше понимают подобные ловушки.

Philippe Mori

Один из примеров, когда ваш код, вероятно, потерпит неудачу, - это CString a = CString() + CString(" World"); То есть если вы замените первый вызов конструктора так, чтобы использовался конструктор по умолчанию.

Также operator= должен вернуть ссылку.

2 Ответов

Рейтинг:
17

hairy_hats

Если вы используете cstr = str; то ваш класс просто содержит указатель на внешнюю строку инициализации, которая может быть изменена вне вашего класса. Если вы возьмете его копию, у вас будет полный контроль над внутренней строкой вашего класса.


Member 11732139

Привет, спасибо за ответ.
можете ли вы дать более подробную информацию на примере того, что вы подразумеваете под "что может быть изменено вне вашего класса" из вашего вопроса

вы имели в виду, что copyconstructor должен иметь const в качестве param?
CString::CString(const CString& q)

hairy_hats

Предположим, у вас было:

char [] hello = " Привет, мир!";

CString string (hello); / / строка содержит "Hello, World!"

strcpy (привет, " До свидания!");

Если вы используете cstr=str; то строка теперь содержит "Goodbye!", даже если вы изменили hello. Если вы выделите свою собственную память и скопируете текст инициализации, то внутренний текст вашего класса не изменится.

Philippe Mori

Еще хуже было бы, если бы данные в стеке или в противном случае были удалены, в то время как строка все еще ссылается на эту копию...

Рейтинг:
0

Jochen Arndt

Со строкой у вас есть некоторая память, где она на самом деле хранится, и у вас есть указатель на эту память, который ссылается на строку. Если вы хотите создать копию строки, вы должны скопировать память, которая требует выделения для нее новой памяти.

В вашем коде также есть некоторые ошибки и возможные оптимизации.

Например вы должны удалить старую память при выделении нового блока и при уничтожении объекта:

CString CString::operator =(const CString &q)
{
    cout << "operator =" << endl;
    if (this != &q)
    {
        // No need to check if cstr is NULL.
        // delete does nothing in this case.
        delete[] cstr;
        // But check here if q.cstr is NULL!
        if (q.cstr)
        {
            cstr = new char[strlen(q.cstr) + 1];
            strcpy(cstr, q.cstr);
        }
        // String is empty now
        else
            cstr = 0;
    }
    return *this;
}


CString::~CString()
{
    delete[] cstr;
}

После того как вы реализовали оператор присваивания, он может быть использован другими функциями:
CString::CString(const CString& q)
{
    cout << " copy constructor" << endl;
    *this = q;
}

Зная это, возможно, было бы лучше иметь CString::CString(const char *s) вместо этого используется оператор присваивания, поскольку он может использоваться для нескольких конструкторов копирования.


Member 11732139

вы имели в виду, что я сделал поверхностную копию?.. для глубокого копирования вам нужно выделить память ..
правильно ли я понимаю!!

Jochen Arndt

Да.
Когда класс содержит выделенную память, вам нужно глубокое копирование (выделение и копирование содержимого).
Неглубокая копия-это копирование содержимого элементов, которые в вашем случае будут Койп-указателем char*.

CDP1802

Да, совершенно верно. Поскольку неглубокая копия нарушила бы инкапсуляцию вашего класса, вы должны сделать глубокую копию.

Philippe Mori

Как правило, лучше реализовать operator= с помощью конструктора копирования и идиомы swap, так как это облегчает написание кода, безопасного для исключений.

Кроме того, в вашем случае конструктор копирования может завершиться неудачей при вызове оператора присваивания, так как cstr еще не инициализирован.

Member 11732139

Спасибо... нашел некоторое улучшение в своем понимании.
мне до сих пор не ясно, почему требуется CString:: CString(const char *s)...!!!

Jochen Arndt

Что непонятно, const или конструктор с char*?

И то, и другое не требуется.

Но имеет смысл иметь строковый класс с конструктором char*.

Const сообщает компилятору (и разработчику, использующему эту функцию), что функция не будет изменять переданную строку. Он всегда должен использоваться, когда переменная не изменяется.