Hạ cấp shared_ptr <Base> thành shared_ptr <Đã xác minh>?


102

Cập nhật: shared_ptr trong ví dụ này giống như trong Boost, nhưng nó không hỗ trợ shared_polymorphic_downcast (hoặc dynamic_pointer_cast hoặc static_pointer_cast cho vấn đề đó)!

Tôi đang cố gắng khởi tạo một con trỏ được chia sẻ tới một lớp dẫn xuất mà không làm mất số lượng tham chiếu:

struct Base { };
struct Derived : public Base { };
shared_ptr<Base> base(new Base());
shared_ptr<Derived> derived;

// error: invalid conversion from 'Base* const' to 'Derived*'
derived = base;  

Càng xa càng tốt. Tôi không mong đợi C ++ chuyển đổi ngầm định Base * thành Derived *. Tuy nhiên, tôi muốn chức năng được thể hiện bởi mã (nghĩa là, duy trì số lượng tham chiếu trong khi dự báo xuống con trỏ cơ sở). Suy nghĩ đầu tiên của tôi là cung cấp một toán tử ép kiểu trong Base để một chuyển đổi ngầm thành Derived có thể diễn ra (đối với người chạy bộ: Tôi sẽ kiểm tra xem việc truyền xuống có hợp lệ không, đừng lo lắng):

struct Base {
  operator Derived* ();
}
// ...
Base::operator Derived* () {
  return down_cast<Derived*>(this);
}

Chà, nó không giúp được gì. Có vẻ như trình biên dịch đã hoàn toàn bỏ qua toán tử typecast của tôi. Bất kỳ ý tưởng nào về cách tôi có thể làm cho bài tập shared_ptr hoạt động? Để biết thêm điểm: loại loại Base* constlà gì? const Base*Tôi hiểu, nhưng Base* const? Điều gì constđề cập đến trong trường hợp này?


Tại sao bạn cần shared_ptr <Derived>, thay vì shared_ptr <Base>?
Hóa đơn

3
Bởi vì tôi muốn truy cập chức năng trong Derived không có trong Base, mà không cần sao chép đối tượng (tôi muốn một đối tượng duy nhất, được tham chiếu bởi hai con trỏ dùng chung). Nhân tiện, tại sao các toán tử ép kiểu không hoạt động?
Lajos Nagy

Câu trả lời:


109

Bạn có thể sử dụng dynamic_pointer_cast. Nó được hỗ trợ bởi std::shared_ptr.

std::shared_ptr<Base> base (new Derived());
std::shared_ptr<Derived> derived =
               std::dynamic_pointer_cast<Derived> (base);

Tài liệu: https://en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

Ngoài ra, tôi không khuyên bạn sử dụng toán tử ép kiểu trong lớp cơ sở. Truyền ngầm như thế này có thể trở thành nguồn gốc của lỗi và lỗi.

-Cập nhật: Nếu loại không đa hình, std::static_pointer_castcó thể được sử dụng.


4
Ngay từ dòng đầu tiên tôi đã không hiểu rằng anh ấy không sử dụng std::shared_ptr. Nhưng từ những bình luận của câu trả lời đầu tiên, tôi suy ra rằng anh ấy không sử dụng boost, vì vậy anh ấy có thể đang sử dụng std::shared_ptr.
Massood Khaari

ĐỒNG Ý. Lấy làm tiếc. Tốt hơn là anh ấy nên làm rõ rằng anh ấy đang sử dụng triển khai tùy chỉnh.
Massood Khaari

47

Tôi cho rằng bạn đang sử dụng boost::shared_ptr... Tôi nghĩ bạn muốn dynamic_pointer_casthoặc shared_polymorphic_downcast.

Tuy nhiên, chúng yêu cầu các loại đa hình.

loại nào là loại Base* const? const Base*Tôi hiểu, nhưng Base* const? Điều gì constđề cập đến trong trường hợp này?

  • const Base *là một con trỏ có thể thay đổi đến một hằng số Base.
  • Base const *là một con trỏ có thể thay đổi đến một hằng số Base.
  • Base * constlà một con trỏ liên tục đến một biến Base.
  • Base const * constlà một con trỏ hằng đến một hằng số Base.

Đây là một ví dụ tối thiểu:

struct Base { virtual ~Base() { } };   // dynamic casts require polymorphic types
struct Derived : public Base { };

boost::shared_ptr<Base> base(new Base());
boost::shared_ptr<Derived> derived;
derived = boost::static_pointer_cast<Derived>(base);
derived = boost::dynamic_pointer_cast<Derived>(base);
derived = boost::shared_polymorphic_downcast<Derived>(base);

Tôi không chắc liệu ví dụ của bạn có cố ý tạo một phiên bản của kiểu cơ sở và sử dụng nó hay không, nhưng nó phục vụ để minh họa sự khác biệt một cách độc đáo.

Ý static_pointer_castchí “cứ làm đi”. Điều này sẽ dẫn đến hành vi không xác định ( Derived*chỉ vào bộ nhớ được cấp phát và khởi tạo bởi Base) và có thể sẽ gây ra sự cố hoặc tệ hơn. Số lượng tham chiếu trên basesẽ được tăng lên.

Các dynamic_pointer_castsẽ dẫn đến một con trỏ null. Số lượng tham chiếu trên basesẽ không thay đổi.

Ý shared_polymorphic_downcastchí có cùng kết quả như một phép ép kiểu tĩnh, nhưng sẽ kích hoạt một xác nhận, thay vì dường như thành công và dẫn đến hành vi không xác định. Số lượng tham chiếu trên basesẽ được tăng lên.

Xem (liên kết chết) :

Đôi khi hơi khó để quyết định sử dụng static_casthoặc dynamic_castvà bạn ước mình có thể có một chút của cả hai thế giới. Ai cũng biết rằng dynamic_cast có chi phí thời gian chạy, nhưng nó an toàn hơn, trong khi static_cast không có chi phí nào cả, nhưng nó có thể bị lỗi một cách âm thầm. Thật tuyệt biết bao nếu bạn có thể sử dụng shared_dynamic_casttrong các bản dựng gỡ lỗi và shared_static_casttrong các bản dựng phát hành. Vâng, một thứ như vậy đã có sẵn và được gọi shared_polymorphic_downcast.


Rất tiếc, giải pháp của bạn phụ thuộc vào chức năng Boost đã được cố tình loại trừ khỏi triển khai shared_ptr cụ thể mà chúng tôi đang sử dụng (đừng hỏi tại sao). Đối với lời giải thích const, bây giờ nó có ý nghĩa hơn nhiều.
Lajos Nagy

3
Thiếu việc triển khai các hàm tạo khác shared_ptr(lấy static_cast_tagdynamic_cast_tag), bạn không thể làm gì nhiều. Bất cứ điều gì bạn làm bên ngoài shared_ptrsẽ không thể quản lý số tiền hoàn lại. - Trong một thiết kế OO "hoàn hảo", bạn luôn có thể sử dụng kiểu cơ sở và không bao giờ cần biết cũng như không quan tâm kiểu dẫn xuất là gì, bởi vì tất cả chức năng của nó được hiển thị thông qua các giao diện lớp cơ sở. Có lẽ bạn chỉ cần nghĩ lại lý do tại sao bạn cần phải giảm giá ngay từ đầu.
Tim Sylvester,

1
@Tim Sylvester: nhưng, C ++ không phải là một ngôn ngữ OO "hoàn hảo"! :-) xuống phôi có chỗ đứng của họ trong một ngôn ngữ OO không hoàn hảo
Steve Folly

4

Nếu ai đó đến đây với boost :: shared_ptr ...

Đây là cách bạn có thể downcast xuống Boost_ptr có nguồn gốc. Giả sử Derived kế thừa từ Base.

boost::shared_ptr<Base> bS;
bS.reset(new Derived());

boost::shared_ptr<Derived> dS = boost::dynamic_pointer_cast<Derived,Base>(bS);
std::cout << "DerivedSPtr  is: " << std::boolalpha << (dS.get() != 0) << std::endl;

Đảm bảo rằng lớp / cấu trúc 'Cơ sở' có ít nhất một hàm ảo. Một trình hủy ảo cũng hoạt động.

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.