2010-03-14 43 views
5

Tôi đang viết một ++ lớp C cho một cuốn sách có chứa một cái tên:thay thế cho strdup

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

Tôi không được phép sử dụng std::string trong nhiệm vụ này. Vì vậy, ở đây tôi đang sử dụng strdup để sao chép các giá trị của tên tham số vào nm trong constructor:

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

Có một sự thay thế của việc đạt được kết quả tương tự mà không sử dụng strdup, nhưng sử dụng các từ khóa new để thay thế?

+2

Thông thường, bạn sẽ sử dụng 'std :: chuỗi' nhưng chính xác những gì bạn có ý nghĩa bởi "không có sử dụng thư viện STL C++"? tức là phần nào của thư viện chuẩn mà bạn đang cố gắng tránh (và tại sao)? –

+0

Tại sao bạn không thể sử dụng 'strdup'? Bạn đang yêu cầu một công cụ để làm điều gì đó trong khi từ chối sử dụng lý tưởng. –

+0

đó là một hạn chế về việc chuyển nhượng ... có lẽ tôi nên thêm một thẻ bài tập ở nhà ... và có bởi STL tôi có nghĩa là sử dụng chuỗi – aherlambang

Trả lời

2

Nói đúng: Lớp string là một phần của thư viện Strings. Điều này dễ sử dụng hơn, năng động hơn và bạn ít lo lắng khi sao chép/gán hơn các chuỗi kiểu C.

Cách tiếp cận khác là để tự sao chép ra:

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

Vấn đề với phương pháp này là bạn sẽ phải quản lý bộ nhớ cho mình và thực hiện tất cả các Big-ba chức năng thành viên đặc biệt cho mình (và đảm bảo ngoại lệ - an toàn nhiều nhất có thể).

+0

Tôi không thể sử dụng chuỗi – aherlambang

+0

Bạn không phải kiểm tra 'if (p)' sau 'mới', bởi vì chỉ không có mới có thể trả về một con trỏ rỗng. Bạn phải giải phóng mảng cũ trong 'operator ='. Sao chép và trao đổi thường tốt hơn. –

+0

Có, thứ hai là lỗi đánh máy. Trước đây tôi muốn đưa vào làm bình luận. (Tôi cho rằng OP đang hoạt động trong một môi trường hạn chế và có thể không có quyền truy cập để xử lý ngoại lệ.) – dirkgently

3

Có một giải pháp thay thế.

  • có được một kích thước của chuỗi
  • tạo một mảng có cùng kích thước như là chuỗi
  • sao chép nội dung của chuỗi thành mảng
  • điểm nm đến mảng phân bổ của bạn

Hoặc bạn có thể sử dụng strdup - btw strdup không phải là một phần của C++ STL.

0

Bạn phải malloc với strlen và sau đó sử dụng strcopy. Bài tập về nhà ngu ngốc btw.

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

bạn quên null ở cuối chuỗi thông qua việc sử dụng memcpy – tony

+0

D'oh! Cảm ơn - nó đã được sửa lại ngay bây giờ – JBRWilkinson

4

Không thực sự là một câu trả lời, nhưng một sự điều chỉnh để của dirkgently rằng sẽ không phù hợp trong một chú thích: bạn thực sự không nên viết quá nhiều mã như ông đã làm.

Sao chép đối tượng an toàn không phải là điều bạn muốn sai quá, mặc dù trong cuộc sống thực, cách tốt nhất để tránh điều đó dĩ nhiên là sử dụng các lớp thư viện thích hợp ngay từ đầu. Điều đó nói rằng, một chuỗi kiểu C đơn giản là một ví dụ tốt như bất kỳ điều gì khác để thực hành với:

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

Xem "không ném ngoại lệ!" cảnh báo trong constructor? Đó là bởi vì nếu bạn làm, chuỗi sẽ bị rò rỉ. Nếu bạn cần nhiều hơn một tài nguyên trong lớp của bạn yêu cầu giải phóng rõ ràng, đó là khi mọi thứ trở nên thực sự tẻ nhạt. Điều đúng đắn cần làm là viết một lớp chỉ với mục đích giữ chuỗi, và một lớp khác với mục đích giữ tài nguyên khác, và có một thành viên của mỗi loại trong lớp Sách của bạn. Sau đó, bạn không phải lo lắng về các ngoại lệ trong hàm tạo, bởi vì các thành viên đã được xây dựng bị phá hủy nếu phần thân hàm tạo của lớp chứa có ném. Một khi bạn đã làm điều này một vài lần, bạn sẽ khá quan tâm để sử dụng các thư viện chuẩn và TR1.

Thông thường, để tiết kiệm công sức bạn muốn bắt đầu bằng cách làm cho lớp học của bạn không copyable, và chỉ thực hiện các constructor sao chép và operator = nếu nó quay ra bạn cần chúng:

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

Dù sao, strdup không bí ẩn lớn. Dưới đây là một vài triển khai rất giống nhau (cả từ GNU), chỉ bằng cách tìm kiếm "strdup.c". Cách tiếp cận tương tự thường hoạt động đối với các hàm xử lý chuỗi khác, và nói chung là không yêu cầu các cơ chế phụ thuộc vào nền tảng đặc biệt để thực hiện: tìm "function_name.c" và có thể bạn sẽ tìm thấy một triển khai GNU giải thích cách thực hiện và cách bạn có thể làm những việc tương tự nhưng khác nhau. Trong trường hợp này, bạn bắt đầu với mã của họ và thay thế cuộc gọi đến malloc và xử lý lỗi.

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 cho an toàn ngoại lệ. – msandiford