Не совсем ответ, но поправка к dirkgently, которая не вписывается в комментарий: вы действительно не должны писать так много кода, как он.
Безопасное копирование объектов - это не то, что вы хотите получить слишком сильно неправильно, хотя в реальной жизни лучший способ избежать этого - это, конечно, сначала использовать соответствующие библиотечные классы. Тем не менее, простая строка в стиле C является таким же хорошим примером, как и все, с чем можно попрактиковаться:
class Book {
char *nm;
public:
Book(const char *name) : nm(copystr(name)) { /* don't throw an exception! */ }
Book(const Book &o) : nm(copystr(o.nm)) { /* Likewise! */ }
~Book() { delete[] nm; }
Book& operator=(const Book &o) {
// this is called copy-and-swap (CAS). If you absolutely
// have to write this kind of resource-managing code, then
// you will need this technique, because it's the best
// way to provide the strong exception guarantee.
Book cp = o;
swap(cp);
return *this;
}
/* or you can do this:
Book& operator=(Book cp) {
swap(cp);
return *this;
}
*/
void swap(Book &o) {
std::swap(this->nm, o.nm);
// also swap other members
}
};
char *copystr(const char *name) {
if (!name) return 0;
char *newname = new char[strlen(name)+1];
std::strcpy(newname, name);
return newname;
}
Смотрите "не бросайте исключение!" предупреждение в конструкторе? Это потому, что если вы это сделаете, строка будет утечка. Если вам нужно более одного ресурса в вашем классе, который требует явного освобождения, тогда вещи становятся действительно утомительными. Правильнее всего написать класс только для удержания строки, а другой - для удержания другого ресурса, и иметь по одному члену каждого типа в вашем классе Book. Тогда вам не нужно беспокоиться об исключениях в конструкторе, потому что созданные члены разрушаются, если выбрасывается тело конструктора содержащего класса. После того, как вы сделали это пару раз, вы очень захотите использовать стандартные библиотеки и TR1.
Обычно, чтобы сэкономить усилия, вы начнете с того, что ваш класс не будет копироваться, и реализуете только конструктор копирования и оператор =, если окажется, что они вам нужны:
class Book {
char *nm;
public:
Book(const char *name) : nm(copystr(name)) { }
~Book() { delete[] nm; }
private:
Book(const Book &o);
Book& operator=(const Book &o);
};
В любом случае, strdup
не является большой загадкой. Вот пара очень похожих реализаций (обе из GNU), просто ища «strdup.c». Такой же подход обычно работает для других функций обработки строк и, в общем, для всего, что не требует специальных механизмов, зависящих от платформы: ищите «function_name.c», и вы, вероятно, найдете реализацию GNU, которая объясняет, как это делается и как вы можете делать похожие, но разные вещи. В этом случае вы начнете с их кода и замените вызов на malloc
и обработку ошибок.
http://www.koders.com/c/fidF16762E3999BA95A0B5D87AECB0525BA67CEE45A.aspx
http://cvs.frodo.looijaard.name/viewvc/cgi-bin/viewvc.cgi/public/psiconv/compat/strdup.c?revision=1.1.1.1&view=markup