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

4

5 回答 5

5

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

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

于 2010-03-14T22:39:46.143 回答
4

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

于 2010-03-14T23:29:54.620 回答
3

是的,有一个替代方案。

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

或者你可以使用strdup-btw strdupis not part of C++ STL.

于 2010-03-14T22:42:37.343 回答
0

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

于 2010-03-14T22:42:40.713 回答
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);
  }
于 2010-03-14T23:20:32.467 回答