2015-01-25 21 views
8

xem xét lớp này:Loại đối tượng nào nên chức năng này trả về?

class Widget 
{ 
    Widget::Widget(); 
    bool initialize(); 
} 

Một Widget có những đặc điểm sau đây:

  1. initialize() phải được viện dẫn để xây dựng đầy đủ
  2. initialize() có thể thất bại
  3. initialize() là tốn kém
.210

Cho rằng, tôi đang đóng gói sáng tạo trong chức năng nhà máy luôn trả về cùng Widget dụ:

Widget* widget() { 
    static auto w = new Widget; 
    static auto initialized = false; 

    if (!initialized) { 
     if (!w->initialize()) { 
      return nullptr; 
     } 
     initialized = true; 
    } 

    return w; 
} 

Cái kiểu trả về của widget() nên được?

Cụ thể, tôi muốn bằng cách nào đó làm rõ rằng tuổi thọ của trả lại Widget sẽ tồn tại lâu hơn bất kỳ người gọi nào, nhưng không tham chiếu đến việc triển khai nội bộ.

  1. Trả về con trỏ thô và thêm nhận xét cho biết "Con trỏ được trả về trỏ tới đối tượng có thời lượng lưu trữ tĩnh sẽ không bị xóa trước khi kết thúc chương trình". Điều này rất đơn giản, nhưng không phải là tự tạo tài liệu.
  2. Trả lại số std::shared_ptr<Widget>. Đây là tự tài liệu, nhưng tôi không thích nó sẽ giới thiệu hoàn toàn không cần thiết tham chiếu đếm trên không.
  3. Trả lại std::unique_ptr<Widget> với chức năng tùy chỉnh deleter là no-op. Tôi nghĩ rằng điều này có cùng một vấn đề nhận thức như # 2 nếu người gọi chuyển đổi nó thành một shared_ptr.
+0

Nó thực sự là một vấn đề quan trọng, nhưng trong trường hợp cụ thể này, tôi sẽ không lo lắng về việc nạp tiền trên cao. Nếu loại Tiện ích con của bạn "đắt tiền" để tạo, hơn bất kỳ "chi phí đếm tài liệu tham khảo không cần thiết" nào có thể là tầm thường bằng cách so sánh. – MrEricSir

+0

Ngôn ngữ C++ không khuyến khích sử dụng giao diện đủ. Nhưng có, họ là một pita hoàn chỉnh để gỡ lỗi, việc triển khai thực hiện rất khó để vượt qua. –

+2

Chúng ta có nên bỏ qua sự đa dạng không tin cậy của điều này? (Ngay cả trong C++ 11 này không an toàn) –

Trả lời

11

Tôi bỏ phiếu cho:

boost::optional<Widget&> widget() { 
    static Widget w; // no reason for this to be a pointer 
    static bool initialized = false; 

    if (!initialized) { 
     if (!w.initialize()) { 
      return boost::none; 
     } 
     initialized = true; 
    } 

    return w; 
} 

Nó làm cho nó rõ ràng rằng người gọi không sở hữu Widget dưới mọi hình thức, không có lo lắng của người gọi delete -ing các Widget, và nó rõ liệu hoặc không gọi thành công.

+0

Còn trong môi trường có tăng cường không? –

+1

@JoshuaJohnson Bạn chỉ có thể sao chép 'tùy chọn' từ đó - đó là thư viện chỉ tiêu đề. Ngoài ra còn có ['std :: experiment :: optional'] (http://en.cppreference.com/w/cpp/experimental/optional). Hoặc bạn chỉ có thể viết lớp của riêng bạn có một thành viên 'Widget *' có thể không có giá trị mà bạn phải dereference để thoát ra. – Barry

+1

tùy chọn thử nghiệm không có hỗ trợ tham chiếu, cuối cùng tôi đã chọn. – Yakk

7

Không phải là con trỏ sống đúng để làm ở đây? Nó thể hiện các hạn chế đã có. Nó có thể thất bại (bằng cách trả về nullptr), và vì nó không đưa ra lời hứa về con trỏ, người gọi không thể xóa nó một cách an toàn. Bạn đang nhận được một con trỏ thô, bạn không thể làm cho giả định rằng bạn được phép thực hiện bất kỳ báo cáo nào về tuổi thọ của đối tượng được trỏ tới.

+0

Tôi nghĩ rằng một con trỏ thô là tốt. Nó ghi lại một thực tế rằng người gọi không được dự kiến ​​sẽ sở hữu và nó có thể là null. Tôi nghĩ rằng bất cứ lựa chọn nào được chọn vẫn cần được làm rõ bằng các bình luận hoặc quy ước đặt tên là đây là một Singleton nên cuộc đời rất rõ ràng. –

1

Đề xuất của Herb Sutter trong trường hợp này (mục 4 tại http://herbsutter.com/2013/05/30/gotw-90-solution-factories/) là trả lại optional.

Có thể có thêm một lý do khiến hàm có thể trả về một con trỏ, cụ thể là trả về nullptr để cho biết không tạo được đối tượng. Thông thường, tốt hơn là nên loại trừ một ngoại lệ để báo cáo lỗi nếu chúng tôi không tải được tiện ích con. Tuy nhiên, nếu không thể tải tiện ích là hoạt động bình thường và không được coi là lỗi, hãy trả lại tùy chọn và có thể khiến nhà máy không nhận biết nếu không có loại lỗi nào khác cần được báo cáo. .

+0

Không phải Herb có nói về trường hợp người gọi có quyền sở hữu đối tượng được trả về không? –

+0

Ah, vâng, tốt. Tôi tự hỏi nếu tốt nhất là xóa câu trả lời này thì sao? –

0

Để làm cho cuộc đời và quyền sở hữu rõ ràng hơn tôi sẽ sử dụng các công ước của Singleton pattern và làm cho chức năng của bạn một getInstance chức năng tĩnh trên lớp Widget.

class Widget { 
    bool initialize(); 
public: 
    static Widget* getInstance() { 
    static Widget w; 
    static bool initialized = false; 

    if (!initialized) { 
     if (!w.initialize()) { 
     return nullptr; 
     } 
     initialized = true; 
    } 
    return &w; 
    } 
}; 

Tôi nghĩ rằng kiểu trả về con trỏ thô thực tế là người gọi không được dự kiến ​​sở hữu và nó có thể là rỗng.

1

Khi người khác lưu ý nếu nhà máy chỉ sản xuất một mặt hàng, nhà máy có lẽ không phải là thời hạn phù hợp. Có vẻ như Singleton.

Lấy trong tài khoản đó:

  • Chúng tôi sẽ tạo ra chỉ có một thể hiện của Widget
  • Đó dụ sẽ được xây dựng lần đầu tiên một người nào đó yêu cầu nó (nếu có)
  • Đó dụ sẽ sống cho đến khi chương trình kết thúc VÀ phải bị phá hủy sau đó
  • Không ai nên xóa dụ

Tôi sẽ thử somethi ng như thế này:

class Widget { 
public: 
    static Widget& Instance() { 
     static Widget w{}; 
     return w; 
    } 

private: 
    Widget() { 
     // Expensive construction 
    } 
    Widget(const Widget&) = delete; // avoid copy 

}; 
+0

Điều này là tốt nhưng nó không xử lý tình huống nơi khởi tạo có thể thất bại. –

Các vấn đề liên quan