替代strdup的方法

7

我正在编写一个包含名称的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来实现相同结果的替代方法?


2
通常情况下,您会使用std::string,但是“不使用C++ STL库”究竟是什么意思?即您要避免使用标准库的哪些部分(以及为什么)? - CB Bailey
为什么你不能只是使用 strdup?你正在寻求一种工具来完成某件事情,却拒绝使用最理想的工具。 - Ben S
这是对赋值的限制...也许我应该在那里加一个作业标签...是的,通过STL我指的是使用字符串。 - aherlambang
1
可能应该再加一个标签不是真正的问题... - stefanB
这个任务在strdup成为标准库的一部分之前肯定已经被构思出来了。毫无疑问,“如果没有strdup,你如何复制一个字符串?”的答案应该是“为什么要这样做呢?” :) - Duncan
显示剩余6条评论
5个回答

6

严格来说:字符串类是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;             
     }
 };

这种方法的问题在于你必须自己管理内存,并自己实现所有的Big-three特殊成员函数(并尽可能确保异常安全性)。

你不必在使用new之后检查if (p),因为只有nothrow new才会返回空指针。你必须在operator=中释放旧数组。通常最好使用复制并交换技术。 - Steve Jessop
是的,第二个是打错了。我原本想把前面那个作为注释。 (我假设OP在受限制的环境中工作,并且可能无法访问异常处理。) - dirkgently
最终意识到你需要一个字符串类,并开始编写它......哦,等等... - tony
谢谢Tony。当我没有在IDE上编码并且没有喝第一杯咖啡时,对我来说有些吃不消了:( - dirkgently
我想亚历山大今天不会学到什么,因为你给了他代码而不是描述步骤 - 对我们有好处,在市场上竞争更少... - stefanB
显示剩余2条评论

5
不是一个真正的答案,而是对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。
通常,为了节省精力,你应该首先使你的类不可复制,并只在需要时实现复制构造函数和operator=:
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

有替代方法。

  • 获取字符串的大小
  • 创建一个与字符串大小相同的数组
  • 将字符串内容复制到该数组中
  • nm指向已分配的数组

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


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

你在使用memcpy时忘记在字符串末尾添加空字符了。 - tony
糟糕!谢谢 - 现在已经修复了。 - JBRWilkinson

0
你必须使用strlen函数分配内存,然后使用strcpy函数。 顺便说一句,这是愚蠢的作业。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接