Я работал на 3 компании до сих пор и только что недавно начал писать "offical" NUnit тесты, и я должен сказать, что я - большой поклонник. Существует 2 точки, которые я заметил однако за последние несколько недель:
Это зависит от приложения - существуют определенные приложения/части приложения, которое может быть очень легко к модульному тесту. Функция, которая делает что-то в основном тот же каждый раз и приводит к легко распознаваемому результату. И существуют разделы, которые не делают этого: блок кода, который печатает кристаллические отчеты или изменяет изображение - здесь глазное яблоко, на самом деле необходим и вероятно лучше/быстрее. Эти области могут отговорить людей пробовать к тестам записи. Они затем только начинают думать это, если определенные области не могут быть протестированной единицей, почему беспокойство?
гранулярность теста - много DEVs/QAs уже имеют в распоряжении своего рода автоматизацию, чтобы 'протестировать' часть функциональности и запутаться относительно того, как это отличается от модульного теста. В зависимости от их теста это не могло бы быть. Важная вещь здесь, которая занимает время для понимания, состоит в том, что хороший модульный тест полностью детализирован. Это тестирует минимальную часть логики, которая имеет смысл, который делает это повторяемым, автоматическим, и надо надеяться всегда допустимым. Это взяло меня немного, чтобы действительно ценить, как полезный то есть, особенно когда Вы имеете огромную кодовую базу и любите выполнять регрессионные тесты после внесения изменения, чтобы видеть если Вы просто foobared целое приложение.
Потому что char *
не является строкой. Это просто указатель на какой-то символ с условием, что могут быть другие символы, а после последнего идет '\ 0'
.
Строковый литерал в C (и, следовательно, в C ++), такой как «abc»
, представляет собой просто массив символов, а компилятор молча добавляет '\ 0'
. Когда вы назначаете массив указателю, массив молча преобразует указатель в первый элемент. В результате
at = "tw";
означает, что указателю на
назначен адрес первого символа в строковом литерале «tw»
. Тем самым он потеряет свою старую ценность. Поскольку это был адрес динамически выделяемого массива символов, вы теряете этот массив.
Когда вы позже назначаете символ в массиве , на который теперь указывает
, вы присваиваете новое значение некоторому символу в строковом литерале. Это вызывает неопределенное поведение и немедленное зависание или сбой программы, вероятно, лучшее, что может случиться с вами, когда вы это сделаете. (На многих платформах вы выполняете запись в постоянную память.)
Позже вы передаете в
в delete []
(и не delete
, поскольку вы назвали новый []
, а не новый
). При этом вы передаете ему адрес строкового литерала вместо выделенного массива символов. Это, конечно, испортит диспетчер кучи. (Библиотека времени выполнения VC улавливает это в режиме отладки.)
std :: strcpy
, с другой стороны, копирует строковый символ за символом из одного массива в другой массив. Никакие указатели не меняются, копируются только куски памяти. Указатель на целевой массив после этого все еще указывает на целевой массив, только данные в этом массиве изменились.
Позвольте мне добавить следующее: Как новичок в C ++, вы должны использовать std :: string
, а не строки C. Это делает всю грязную работу за вас и имеет разумную семантику.
Вы ошиблись в двух вещах: заставляете указатель указывать на что-то другое (это то, что делает присваивание) и копируете некоторые данные в место, указанное указателем.
at = "tw";
этот код делает в
указывают на буквальный "tw", созданный где-то в постоянной памяти. Попытка записать в него - неопределенное поведение.
char *at = new char [3];
strcpy(at,"t");
этот код выделяет память для трех символов и заставляет в
указывать на эту часть памяти (строка 1), а затем копирует некоторые данные в память, указанную ] в
.
И помните, что память, выделенная с помощью new []
, должна быть освобождена с помощью delete []
, а не delete
Я вам советую чтобы узнать больше об указателях. Это обсуждение охватывает это.
Когда вы делаете
char *at = ...;
at = "hello";
, вы в основном перезаписываете значение указателя (т. Е. Адрес памяти, выделенной для вас new []
) с адресом статическая постоянная строка. Это означает, что когда вы позже удаляете из этой памяти, вы передаете delete
указатель, который ранее не возвращался new
.
Это плохо для
В C и C ++ назначения указателям обычно ничего не делают с памятью, на которую указывает указатель, они изменяют сам указатель. Это может сбивать с толку, если вы привыкли к языку, в котором строки больше относятся к «гражданам первого сорта».
Кроме того, вы должны использовать delete []
, если вы использовали new []
.
Необходимо понять 3 вещи:
1) char * at;
- это просто переменная-указатель.
Переменная-указатель просто означает, что она содержит адрес памяти.
2) new char [3]
возвращает начальный адрес памяти, выделенной в куче.
3) «hello»
возвращает адрес строкового литерала.
char *at = new char [3];
//at now contains the address of the memory allocated on the heap
at = "hello";
//at now contains the address of the static string.
// (and by the way you just created a 3 byte memory leak)
delete[] at;
//WOOPS!!!! you can't do that because you aren't deleting
// the original 3 chars anymore which were allocated on the heap!
//Since at contains the string literal's memory address you're
// trying to delete the string literal.
Примечание об изменении постоянной памяти для чтения:
Также вы никогда не должны изменять строковый литерал. Т.е. этого никогда не следует делать:
char *at = "hello";
at[2] = '\0';
Память для строковых литералов должна быть доступна только для чтения, и если вы ее измените, результаты не будут определены языком C ++.
Поскольку вы используете C ++:
Поскольку вы используете C ++, рассмотрите возможность использования вместо него типа std :: string
.
#include <string>
using namespace std;
int main(int argc, char **argv)
{
string s = "hello";
s += " world!";
//s now contains "hello world!"
s = "goodbye!";
//Everything is still valid, and s contains "goodbye!"
//No need to cleanup s.
return 0;
}
Не забывайте использовать
delete []
всякий раз, когда вы выделяете что-то с помощью [].
Указатель содержит адрес. Оператор = для указателя изменяет удерживаемый адрес.
at = "tw";
Указывает на массив "tw" (массив, созданный компилятором для хранения символов tw), он больше не указывает на массив, созданный с помощью new. создается в файле.
at[2] = '\0';
Добавляет NULL в конец массива компилятора.
В первом примере вы меняете значение, во втором вы меняете значение того, на что указывает. Присвоение символа char * строке с двойными кавычками назначает его статическому указателю на константу.
В частности, в первом примере at now указывает на другое место в памяти.
В вашем первом примере вы выделяете некоторую память и указываете на нее с помощью переменной «at». Когда вы выполняете
at = "tw"
, вы фактически перенаправляете символ * на постоянную строку символов. Это вызывает утечку памяти. Когда вы продолжаете удалять «at», вы пытаетесь удалить стековую память.
strcpy просматривает каждый символ и копирует их значения в новую память, которую вы выделяете. Это также известно как глубокая копия.
В первом примере вы вызвали утечку памяти.
Ваша переменная в
равна указатель на адрес памяти, а не на саму строку. Когда вы назначаете указателю адрес «tw»
, вы теряете исходный адрес, который вы получили с помощью new
. at
теперь указывает на адрес, который вы не выделили с помощью new
, поэтому вы не можете удалить
его.
Если вы думаете об указателях как о целых числах, это вероятно будет иметь больше смысла. Я назначил произвольные числа в качестве адресов для обсуждения.
char *at = new char[3]; // 0x1000
at = "tw"; // 0x2000
at[2] = '\0'; // set char at 0x2002 to 0
delete at; // delete 0x2000 (whoops, didn't allocate that!)