Как я могу предотвратить необходимость копирования строк, переданных конструктору avr-gcc C++?
Вопрос
в ArduinoUnit Библиотека модульного тестирования Я предоставил механизм для присвоения имени TestSuite.Пользователь библиотеки может написать следующее:
TestSuite suite("my test suite");
// ...
suite.run(); // Suite name is used here
Это ожидаемое использование: имя TestSuite представляет собой строковый литерал.Однако, чтобы предотвратить труднообнаружимые ошибки, я чувствую себя обязанным учитывать различные варианты использования, например:
char* name = (char*) malloc(14);
strcpy(name, "my test suite");
TestSuite suite(name);
free(name);
// ...
suite.run(); // Suite name is used here
Таким образом, я реализовал TestSuite следующим образом:
class TestSuite {
public:
TestSuite(const char* name) {
name_ = (char*) malloc(strlen(name) + 1);
strcpy(name_, name);
}
~TestSuite() {
free(name_);
}
private:
char* name_;
};
Оставляя в стороне проблему невозможности справиться с ошибками выделения памяти в конструкторе, я бы предпочел просто выделить указатель на переменную-член следующим образом:
class TestSuite {
public:
TestSuite(const char* name) : name_(name) {
}
private:
const char* name_;
};
Есть ли способ изменить интерфейс, чтобы заставить его использоваться «правильно», чтобы я мог покончить с динамическим распределением памяти?
Решение
Что если вы предоставите два перегруженных конструктора?
TestSuite(const char* name) ...
TestSuite(char* name) ...
При вызове с const char*
конструктор может сделать копию указателя, предполагая, что строка не исчезнет. Если вызывается с char*
, конструктор может сделать копию всей строки.
Обратите внимание, что все еще возможно подорвать этот механизм, передавая name
конструктору, когда <=> фактически выделяется динамически. Однако этого может быть достаточно для ваших целей.
Я должен отметить, что я никогда не видел, чтобы эта техника использовалась в API, это была просто мысль, которая пришла мне в голову, когда я читал ваш вопрос.
Другие советы
Документация.Например,
/**
* Test suite constructor.
* @param name test suite name cstring, shared
*/
TestSuite(char const *name) {
// ...
Общий указатель подразумевает, что указанный объект должен быть активным в течение всего времени существования этого объекта.
Ну, вы можете использовать std :: string, которая позаботится обо всем распределении памяти
class TestSuite {
public:
TestSuite(const std::string &name):name_(name) {
}
~TestSuite() {
}
private:
std::string name_;
};
Изменить . Если вы хотите избежать вызова malloc (), вы можете сделать это:
class TestSuite {
public:
TestSuite(const char *name){
memcpy(name_, name, min(16, strlen(name));
}
private:
char name_[16];
};
Однако это приведет к потере некоторой памяти, что может быть проблемой на встроенных платформах.
Имейте char name[XYZ]
& nbsp; член вашего TestSuite (с достаточно большим XYZ для удобного отображения имени) и используйте strncpy
для копирования строки (с максимальной длиной XYZ-1). р>
Почему вы используете char * и malloc, когда у вас есть хороший строковый класс C ++, который может принимать строковый литерал или char * в своем конструкторе?
Вы можете использовать std :: string? Вы можете сделать это как std::string name_
, и STL позаботится о выделении памяти для вас ..
class TestSuite {
public:
TestSuite(const char* name) : name_(name) {}
~TestSuite() {}
private:
std::string name_;
};
Не забудьте включить <string>
.