Naprawdę nie jest to odpowiedź, ale korekta, która nie pasuje do komentarza: naprawdę nie powinieneś pisać tak dużo kodu jak on.
Bezpieczne kopiowanie obiektów to nie jest coś, co chcemy zbytnio zepsuć, chociaż w rzeczywistości najlepszym sposobem na uniknięcie tego jest oczywiście skorzystanie z odpowiednich klas bibliotecznych. Powiedział, że to prosty C-style łańcuch jest tak dobry przykład jak cokolwiek innego ćwiczyć z: „nie wyjątek”
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;
}
Zobacz ostrzeżenie w konstruktorze? To dlatego, że jeśli to zrobisz, ciąg wycieknie. Jeśli potrzebujesz więcej niż jednego zasobu w swojej klasie, który wymaga jawnego uwolnienia, wtedy sytuacja staje się naprawdę nudna. Właściwą rzeczą jest napisanie klasy tylko w celu trzymania napisu, a innej w celu trzymania drugiego zasobu i posiadania jednego członka każdego typu w klasie Book. Wtedy nie musisz martwić się wyjątkami w konstruktorze, ponieważ elementy, które zostały skonstruowane, są niszczone, jeśli ciało konstruktora klasy zawierającej rzuca. Gdy zrobisz to już kilka razy, będziesz bardzo chciał korzystać ze standardowych bibliotek i TR1.
Normalnie, aby zaoszczędzić wysiłku chcesz zacząć od klasa non-copyable, a tylko wdrożyć konstruktor kopiujący i operator =, jeśli okaże się, że ich potrzebujesz:
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);
};
W każdym razie, nie jest strdup
wielka tajemnica. Oto kilka bardzo podobnych implementacji (zarówno z GNU), po prostu wyszukując "strdup.c". To samo podejście zwykle działa w przypadku innych funkcji obsługi ciągów i ogólnie wszystkiego, co nie wymaga specjalnych, zależnych od platformy mechanizmów do wdrożenia: poszukaj "nazwa_funkcji.c", a prawdopodobnie znajdziesz implementację GNU, która wyjaśni, jak to się robi i jak możesz robić podobne, ale różne rzeczy. W takim przypadku zacznij od kodu i zastąp połączenie malloc
i obsługę błędów.
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
Zazwyczaj należy użyć 'std :: string' ale co dokładnie masz na myśli przez "bez korzystania z biblioteki STL C++"? tj. które części standardowej biblioteki próbujesz uniknąć (i dlaczego)? –
Dlaczego nie możesz po prostu użyć 'strdup'? Pytasz o narzędzie do zrobienia czegoś, odmawiając użycia idealnego. –
to ograniczenie na zlecenie ... może powinienem dodać tag pracy domowej ... i tak przez STL mam na myśli użycie ciągu znaków – aherlambang