Làm cách nào để xử lý các thay đổi thiết kế đối với khấu hao auto_ptr trong C ++ 11?


12

Chúng tôi đang thử nghiệm một thư viện theo C ++ 11 (tức là -std=c++11). Thư viện sử dụng auto_ptrvà mẫu này:

Foo* GetFoo()
{
    autoptr<Foo> ptr(new Foo);

    // Initialize Foo
    ptr->Initialize(...);

    // Now configure remaining attributes
    ptr->SomeSetting(...);

    return ptr.release();
}

C ++ 11 không dùng nữa auto_ptr, vì vậy chúng tôi muốn tránh xa nó.

Tuy nhiên, mã này hỗ trợ cả C ++ 03 và C ++ 11, vì vậy nó không đơn giản như việc kéo dài auto_ptr. Điều đáng nói là thư viện không có sự phụ thuộc bên ngoài. Nó sử dụng C ++ 03; và không sử dụng Autotools, Cmake, Boost, ...

Chúng ta nên xử lý các thay đổi thiết kế như thế nào để tránh xa auto_ptrC ++ 11 trong khi vẫn duy trì khả năng tương thích với C ++ 03?


Có bất kỳ trong auto_ptrphạm vi (nghĩa là std::auto_ptr), chúng cần phải hoặc có thể lấy được con trỏ thông minh từ một số không gian tên khác không?
Niall

Như một bên, bạn có thể muốn gấp Foo::Initializevào Foo::Foo.
MSalters

1
@MSalters - yeah, đó luôn là một trong những điều tôi cảm thấy hơi khó chịu. Thư viện được thiết kế vào những năm 1990 và tôi nghĩ rằng thiết kế tương tự như MFC. Đó là, đã có cấu trúc C ++ cấp thấp hơn, và sau đó là xây dựng đối tượng "cấp cao hơn". Tôi nghĩ rằng tính năng này đã được sử dụng như một sự đánh đổi để các lớp không có 6 hoặc 12 nhà xây dựng khác nhau. (Tại thời điểm này, những gì tôi đã thực hiện đã được thực hiện và đảm bảo các biến thành viên của các loại POD được khởi tạo để mặc định lành mạnh trong các hàm tạo C ++).

Câu trả lời:


13

Trong hầu hết các khía cạnh, std::unique_ptrđã được thực hiện để thay thế (nhưng an toàn hơn) std::auto_ptr, do đó, sẽ có rất ít (nếu có) thay đổi mã được yêu cầu ngoài (như bạn yêu cầu) chỉ đạo mã sử dụng unique_ptrhoặc auto_ptr.

Có một vài cách để làm điều này (và mỗi cách đi kèm với sự đánh đổi danh sách riêng) bên dưới. Với mẫu mã được cung cấp, tôi sẽ ưu tiên một trong hai tùy chọn đầu tiên .

lựa chọn 1

#if __cplusplus >= 201103L
template <typename T>
using auto_ptr = std::unique_ptr<T>;
#else
using std::auto_ptr;
#endif

Đánh đổi;

  • Bạn giới thiệu auto_ptrtên vào không gian tên toàn cầu; bạn có thể giảm thiểu điều này bằng cách xác định nó là không gian tên "riêng tư" của riêng bạn
  • Khi đã chuyển sang C ++ 17 (tôi tin rằng auto_ptrsẽ bị xóa hoàn toàn), bạn có thể dễ dàng tìm kiếm và thay thế hơn

Lựa chọn 2

template <typename T>
struct my_ptr {
    #if __cplusplus >= 201103L
    typedef std::unique_ptr<T> ptr;
    #else
    typedef std::auto_ptr<T> ptr;
    #endif
};

Đánh đổi;

  • Có lẽ cồng kềnh hơn để làm việc với, tất cả các auto_ptrnhu cầu hiện tại cần thay đổi trong mã thành một cái gì đó nhưmy_ptr<T>::ptr
  • An toàn hơn những cái tên không được đưa vào không gian tên toàn cầu

Lựa chọn 3

Hơi gây tranh cãi, nhưng nếu bạn chuẩn bị đưa ra những cảnh báo về việc có một stdlớp học làm cơ sở

#if __cplusplus >= 201103L
template <typename T>
using my_ptr = std::unique_ptr<T>;
#else
template <typename T>
class my_ptr : public std::auto_ptr<T> {
  // implement the constructors for easier use
  // in particular
  explicit my_ptr( X* p = 0 ) : std::auto_ptr(p) {}
};
#endif

Đánh đổi;

  • Đừng cố sử dụng lớp kế thừa nơi mà một cơ sở ảo (cụ thể là viết ra hàm hủy không ảo) sẽ được mong đợi. Không phải đây là một vấn đề trong trường hợp - nhưng hãy lưu ý về nó
  • Một lần nữa, mã thay đổi
  • Không phù hợp không gian tên tiềm năng - tất cả phụ thuộc vào cách lớp con trỏ được sử dụng để bắt đầu

Lựa chọn 4

Gói các con trỏ trong một lớp mới và tổng hợp các hàm cần thiết cho thành viên

template <typename T>
class my_ptr { // could even use auto_ptr name?
  #if __cplusplus >= 201103L
  std::unique_ptr<T> ptr_;
  #else
  std::auto_ptr<T> ptr_;
  #endif

  // implement functions required...
  T* release() { return ptr_.release(); }
};

Đánh đổi;

  • Một chút cực đoan khi tất cả những gì bạn thực sự muốn là "trao đổi" các triển khai

Câu trả lời rất hay. Tôi thực sự đã nghiên cứu nó một chút, và bạn đã đạt ít nhất ba trong số các bài kiểm tra tôi đã thử. (Thứ bạn thiếu là thứ cụ thể của OS X và Clang. OS X là một con gấu vì nó vẫn sử dụng không gian tên TR1 cho C ++ 03 và bạn phải bao gồm những thứ sử dụng phương thức này: Không có loại có tên 'unique_ptr' trong không gian tên 'std' khi biên dịch theo LLVM / Clang ).

@jww. Tôi đang dùng OS X (XCode 6.4 và Apple LLVM phiên bản 6.1.0 (clang-602.0.53) (dựa trên LLVM 3.6.0svn)) và không có vấn đề gì với hỗn hợp C ++ 03/11 ngoài tr1không gian tên còn ở đó nữa (tôi sử dụng libc ++ chứ không phải libstdc ++). Tôi biết tr1 là không quy tắc, nhưng tôi không thể tìm thấy bất cứ nơi nào trong bản nháp (ở đây) rằng các tệp phải <tr1/...>ở tất cả, vì nó đề cập đến việc chỉ nằm trong tiêu đề, <memory>vv chỉ trong tr1không gian tên.
Niall

@jww. Tôi đoán rằng với một sự kết hợp cụ thể của trình biên dịch, thư viện và thiết bị đích - bạn có thể cần phải thực hiện thêm một vài thao tác. Khác, trên OS X, xem xét chuyển sang clang và libc ++. Thành thật mà nói tôi coi libc ++ là thư viện C ++ "bản địa" mới cho OS X - tôi sẽ mặc định điều đó. Tôi không có cách nào để hỗ trợ cho những tuyên bố này rằng lịch sử của mối quan hệ clang / Apple và các công cụ GCC trên OS X dường như đã lỗi thời (thư viện) hoặc chỉ bị xóa (theo như tôi biết GCC vẫn còn sơ khai. ).
Niall

"Khác, trên OS X, xem xét chuyển sang clang và libc ++ ..." - vâng, tôi đồng ý với bạn. Tuy nhiên, chúng tôi muốn cho phép người dùng đưa ra lựa chọn đó và không ép buộc họ. (Họ ngầm đưa ra lựa chọn khi họ chỉ định (hoặc thiếu) CXX=...).

Đây là trường hợp gây ra cho tôi rất nhiều rắc rối trên OS X 10.7 và 10.8 : c++ -v -std=c++11 -x c++ - < /dev/null. Tôi grep'dbao gồm các thư mục đã được kết xuất, và chúng không bao gồm unique_ptr.

0

Tùy chọn 5: Bí danh trực tiếp.

#if __cplusplus >= 201103L
template<typename T> 
using MyPtr = std::unique_ptr<T>;
#else 
#define MyPtr std::auto_ptr
#endif 

Đánh đổi:

  1. Đối với các phiên bản ngôn ngữ mới hơn, AKA C ++ 11 trở lên, loại bí danh của bạn ánh xạ tới con trỏ thông minh chính xác. Bất kỳ mã người dùng nào thực sự phụ thuộc vào API cụ thể cho std :: auto_ptr sẽ bị trình biên dịch gắn cờ, đó là sự đảm bảo cuối cùng rằng nó sẽ thực sự được sửa.

  2. Trong chế độ Legacy c ++ 03, bí danh loại là macro. Đây là tổng, nhưng cú pháp kết quả MyPtr<T>sẽ giống hệt với trường hợp C ++ 11 trong suốt phần còn lại của mã.

  3. Bạn phải tìm và thay đổi tất cả các biến auto_ptr của mình thành MyPtrđể thiết lập điều này.


1
Không rõ điều này có liên quan gì (và, như đã nói, đây không phải là một câu hỏi).
autophage

1
@autophage Tôi tin rằng đó là một câu trả lời ... nên có lẽ không phải là một câu hỏi.
Kain0_0
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.