strdup 的替代方案

发布于 2024-08-25 02:35:52 字数 506 浏览 12 评论 0原文

我正在为一本包含名称的书编写一个 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 代替?

I'm writing a C++ class for a book that contains a name:

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

I am not allowed to use std::string in this assignment. So here I am using strdup to copy the value of the parameter name into nm in the constructor:

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

Is there an alternative of achieving the same result without using strdup, but using the keyword new instead?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

迷爱 2024-09-01 02:35:52

严格来说: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;             
     }
 };

这种方法的问题是您必须自己管理内存并自己实现所有三大特殊成员函数(并尽可能确保异常安全)。

Strictly speaking: The string class is part of the Strings library. This is much easier to use, dynamic in nature and you have less worry when copying/assigning than C-style strings.

The other approach is to manually copy out:

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

The problem with this approach is that you will have to manage the memory yourself and implement all the Big-three special member functions yourself (and ensure exception-safety as much as you can).

余生共白头 2024-09-01 02:35:52

不是真正的答案,而是对不适合评论的 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

Not really an answer, but a correction to dirkgently's that won't fit in a comment: you really shouldn't write so much code as he did.

Safe object copying isn't something you want to get too badly wrong, although in real life the best way to avoid that is of course to use the appropriate library classes in the first place. That said, a simple C-style string is as good an example as anything else to practice with:

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

See the "don't throw an exception!" warning in the constructor? That's because if you do, the string will be leaked. If you need more than one resource in your class that requires explicit freeing, that's when things become really tedious. The right thing to do is to write a class just for the purpose of holding the string, and another one for the purpose of holding the other resource, and have one member of each type in your Book class. Then you don't have to worry about exceptions in the constructor, because members which have been constructed are destructed if the constructor body of the containing class throws. Once you've done this a couple of times, you'll be pretty keen to use the standard libraries and TR1.

Normally, to save effort you'd start by making your class non-copyable, and only implement the copy constructor and operator= if it turns out you need them:

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

Anyway, strdup is no great mystery. Here are a couple of very similar implementations (both from GNU), just by searching for "strdup.c". The same approach usually works for other string-handling functions, and in general anything that doesn't require special platform-dependent mechanisms to implement: look for "function_name.c" and you'll probably find a GNU implementation that explains how it's done, and how you can do similar but different things. In this case you'd start with their code and replace the call to malloc and the error-handling.

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

夜唯美灬不弃 2024-09-01 02:35:52

是的,还有另一种选择。

  • 获取字符串的大小,
  • 创建一个与字符串大小相同的数组,将
  • 字符串的内容复制到该数组
  • nm 到您分配的位置array

或者您可以使用 strdup - 顺便说一句,strdup 不是 C++ STL 的一部分。

Yes there is an alternative.

  • get a size of string
  • create an array of the same size as is the string
  • copy the contents of the string into that array
  • point nm to your allocated array

Or you could use strdup - btw strdup is not part of C++ STL.

看透却不说透 2024-09-01 02:35:52

您必须使用 strlen 进行 malloc,然后使用 strcopy。顺便说一句,愚蠢的家庭作业。

You have to malloc with strlen and then use strcopy. Stupid homework btw.

孤檠 2024-09-01 02:35:52
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);
  }
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);
  }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文