Come posso evitare la necessità di copiare le stringhe passate a un costruttore C ++ avr-gcc?
Domanda
Nella ArduinoUnit ho fornito un meccanismo per dare un nome a TestSuite . Un utente della libreria può scrivere quanto segue:
TestSuite suite("my test suite");
// ...
suite.run(); // Suite name is used here
Questo è l'uso previsto: il nome di TestSuite è letterale stringa. Tuttavia, per prevenire bug difficili da trovare, mi sento obbligato a soddisfare usi diversi, ad esempio:
char* name = (char*) malloc(14);
strcpy(name, "my test suite");
TestSuite suite(name);
free(name);
// ...
suite.run(); // Suite name is used here
Come tale ho implementato TestSuite in questo modo:
class TestSuite {
public:
TestSuite(const char* name) {
name_ = (char*) malloc(strlen(name) + 1);
strcpy(name_, name);
}
~TestSuite() {
free(name_);
}
private:
char* name_;
};
Mettendo da parte il problema di non riuscire a gestire gli errori di allocazione della memoria nel costruttore, preferirei semplicemente assegnare il puntatore a una variabile membro come questa:
class TestSuite {
public:
TestSuite(const char* name) : name_(name) {
}
private:
const char* name_;
};
Esiste un modo per cambiare l'interfaccia per costringerla ad essere utilizzata "correttamente" in modo da poter eliminare l'allocazione dinamica della memoria?
Soluzione
Cosa succede se si forniscono due costruttori sovraccarichi?
TestSuite(const char* name) ...
TestSuite(char* name) ...
Se chiamato con un const char*
, il costruttore potrebbe creare una copia del puntatore, supponendo che la stringa non scompaia. Se chiamato con un char*
, il costruttore potrebbe creare una copia dell'intera stringa.
Si noti che è ancora possibile sovvertire questo meccanismo passando un name
al costruttore quando <=> è effettivamente allocato dinamicamente. Tuttavia, questo potrebbe essere sufficiente per i tuoi scopi.
Dovrei notare che non ho mai visto questa tecnica utilizzata in un'API, è stato solo un pensiero che mi è venuto in mente mentre leggevo la tua domanda.
Altri suggerimenti
Documentazione. Ad esempio,
/**
* Test suite constructor.
* @param name test suite name cstring, shared
*/
TestSuite(char const *name) {
// ...
Un puntatore condiviso implica che l'oggetto appuntito deve essere attivo durante la vita di questo oggetto.
Bene, puoi usare una stringa std :: che si occuperà di tutta l'allocazione della memoria
class TestSuite {
public:
TestSuite(const std::string &name):name_(name) {
}
~TestSuite() {
}
private:
std::string name_;
};
Modifica : Se è la chiamata a malloc () che vuoi evitare, puoi farlo:
class TestSuite {
public:
TestSuite(const char *name){
memcpy(name_, name, min(16, strlen(name));
}
private:
char name_[16];
};
Ciò sprecherà tuttavia un po 'di memoria, il che può essere un problema su piattaforme integrate.
Avere un char name[XYZ]
& nbsp; membro di TestSuite (con una XYZ abbastanza grande da visualizzare comodamente il nome) e usare strncpy
per copiare la stringa (con una lunghezza massima di XYZ-1).
Perché stai usando char * e malloc quando hai la bella classe di stringhe C ++ che può prendere una stringa letterale o un char * nel suo costruttore?
Sei in grado di usare std :: string? Potresti averlo come std::string name_
e far sì che l'STL si occupi dell'allocazione di memoria per te ..
class TestSuite {
public:
TestSuite(const char* name) : name_(name) {}
~TestSuite() {}
private:
std::string name_;
};
Non dimenticare di includere <string>
.