2010-03-14 26 views
5

Piszę klasy C++ dla książki, która zawiera nazwę:alternatywą strdup

class Book { 
private: 
    char* nm; 
.......... 
............ 
.......... 
........... 
}; 

nie wolno mi używać std::string w tym zadaniu. Więc używam strdup skopiować wartość nazwy parametru w nm w konstruktorze:

Book::Book(const char *name, int thickness, int weight) 
    : nm(NULL) 
    , thck(thickness) 
    , wght(weight) 
{ 
    if (name) 
     nm = strdup(name); 
} 

Czy istnieje alternatywa do osiągnięcia tego samego rezultatu bez użycia strdup, ale za pomocą słowa kluczowego new zamiast?

+2

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)? –

+0

Dlaczego nie możesz po prostu użyć 'strdup'? Pytasz o narzędzie do zrobienia czegoś, odmawiając użycia idealnego. –

+0

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

Odpowiedz

2

Ściśle mówiąc: Klasa string jest częścią biblioteki Ciągi. Jest to znacznie prostsze w użyciu, dynamiczne z natury i mniej martwisz się podczas kopiowania/przypisywania niż ciągi w stylu C.

Innym podejściem jest ręcznie skopiować z:

class Book { 
    public: 
    Book(const char *name, ...) : nm(0), ... { 
      if (!name) throw "invalid parameter"; 
      nm = new char [ strlen(name) + 1 ]; 
      strcpy(nm, name); 
    } 
    ~Book() { 
      delete [] nm; 
      // ... 
    } 
    Book(Book const& o) : nm(0), ... { 
      if (!name) throw "invalid parameter"; 
      char *p = new char [ strlen(name) + 1 ]; 
      if (p) { 
       strcpy(p, name); 
       delete [] nm; 
       nm = p; 
      } 
    } 
    Book& operator=(Book const& o) { 
      if (this != &o) { 
       char *p = new char [ strlen(name) + 1 ]; 
       if (p) { 
       strcpy(p, name); 
       delete [] nm; 
       nm = p; 
       } 
      } 
      return *this;    
    } 
}; 

Problem z tego podejścia jest to, że trzeba będzie zarządzać pamięci siebie i realizować wszystkie wielkie trzy specjalne funkcje członka siebie (i zapewnić wyjątek - bezpieczeństwo, jak możesz).

+0

Nie mogę użyć ciągu – aherlambang

+0

Nie trzeba sprawdzać 'if (p)' po 'nowym', ponieważ tylko nothrow new może zwrócić wskaźnik zerowy. Musisz zwolnić starą tablicę w 'operator ='. Kopiowanie i zamiana jest zwykle lepsze. –

+0

Tak, drugi był literówka. Ten pierwszy chciałem dodać jako komentarz. (Założono, że OP działa w ograniczonym środowisku i może nie mieć dostępu do obsługi wyjątków.) – dirkgently

3

Tak, jest alternatywa.

  • uzyskać wielkość napisu
  • utworzyć tablicę o takiej samej wielkości jak to ciąg
  • skopiować zawartość ciąg do tej tablicy
  • punktu nm do przydzielonej tablicy

Lub możesz użyć strdup - btw strdup nie jest częścią C++ STL.

0

Musisz zdekompletować za pomocą strlen, a następnie użyć strcopy. Głupia praca domowa przy okazji.

0
Book::Book(const char *name, int thickness, int weight):nm(NULL), thck(thickness), wght(weight){ 
    if (name) { 
    size_t length = strlen(name); 
    nm = new char[length + 1]; 
    memcpy(nm, name, length + 1); 
    } 
+0

zapomniałeś null na końcu łańcucha za pomocą memcpy – tony

+0

D'oh! Dzięki - jest teraz naprawiony – JBRWilkinson

4

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

+0

+1 dla bezpieczeństwa wyjątków. – msandiford

Powiązane problemy