альтернатива strdup - PullRequest
       9

альтернатива strdup

5 голосов
/ 15 марта 2010

Я пишу класс C ++ для книги, которая содержит имя:

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

Мне не разрешено использовать std::string в этом назначении. Поэтому здесь я использую strdup для копирования значения имени параметра в nm в конструкторе:

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

Есть ли альтернатива достижения того же результата без использования strdup, но с использованием ключевого слова new вместо?

Ответы [ 5 ]

4 голосов
/ 15 марта 2010

Не совсем ответ, но поправка к 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

3 голосов
/ 15 марта 2010

Да, есть альтернатива.

  • получить размер строки
  • создать массив того же размера, что и строка
  • скопировать содержимое строки в этот массив
  • указывает nm на выделенный массив

Или вы можете использовать strdup - кстати, strdup не является частью C++ STL.

3 голосов
/ 15 марта 2010

Строго говоря: класс string является частью библиотеки Strings. Это намного проще в использовании, оно динамическое по своей природе, и вы меньше беспокоитесь при копировании / назначении, чем строки в стиле C.

Другой подход заключается в ручном копировании:

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;             
     }
 };

Проблема с этим подходом заключается в том, что вам придется самостоятельно управлять памятью и самостоятельно реализовывать все специальные функции-члены Большой тройки (и максимально обеспечивать безопасность исключений).

0 голосов
/ 15 марта 2010
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 голосов
/ 15 марта 2010

Вы должны malloc с strlen и затем использовать strcopy. Кстати, глупая домашняя работа.

...