Câu hỏi Khi nào static_cast, dynamic_cast, const_cast và reinterpret_cast được sử dụng?


Cách sử dụng thích hợp của:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • Dàn diễn viên kiểu C (type)value
  • Dàn diễn viên chức năng type(value)

Làm thế nào để quyết định sử dụng trong trường hợp cụ thể nào?


2052
2017-12-01 20:11


gốc


Có lẽ một tài liệu tham khảo tốt ở đây:Làm thế nào để bạn giải thích sự khác biệt giữa static_cast, reinterpret_cast, const_cast, và dynamic_cast đến một lập trình C ++ mới?. - Nan Xiao
Đối với một số ví dụ cụ thể hữu ích về cách sử dụng các loại phôi khác nhau, bạn có thể kiểm tra câu trả lời đầu tiên về một câu hỏi tương tự trong chủ đề khác này. - TeaMonkie


Các câu trả lời:


static_cast là diễn viên đầu tiên bạn nên cố gắng sử dụng. Nó làm những việc như chuyển đổi ngầm giữa các loại (chẳng hạn như int đến float, hoặc con trỏ đến void*), và nó cũng có thể gọi các hàm chuyển đổi rõ ràng (hoặc các hàm ẩn). Trong nhiều trường hợp, nêu rõ static_cast không cần thiết, nhưng điều quan trọng cần lưu ý là T(something) cú pháp tương đương với (T)something và nên tránh (nhiều hơn về điều đó sau). A T(something, something_else) là an toàn, tuy nhiên, và được bảo đảm để gọi cho nhà xây dựng.

static_cast cũng có thể truyền qua phân cấp thừa kế. Nó là không cần thiết khi đúc lên trên (đối với một lớp cơ sở), nhưng khi đúc xuống nó có thể được sử dụng miễn là nó không truyền qua virtual di sản. Nó không kiểm tra, tuy nhiên, và nó là hành vi không xác định static_cast xuống một hệ thống phân cấp thành một loại không thực sự là loại đối tượng.


const_cast có thể được sử dụng để xóa hoặc thêm const cho một biến; không có diễn viên C ++ nào khác có khả năng loại bỏ nó (thậm chí không reinterpret_cast). Điều quan trọng cần lưu ý là sửa đổi trước đây const giá trị chỉ không xác định nếu biến ban đầu là const; nếu bạn sử dụng nó để lấy const một tham chiếu đến nội dung không được khai báo const, an toàn rồi. Điều này có thể hữu ích khi quá tải các hàm thành viên dựa trên const, ví dụ. Nó cũng có thể được sử dụng để thêm const với một đối tượng, chẳng hạn như gọi quá tải hàm thành viên.

const_cast cũng hoạt động tương tự trên volatile, mặc dù ít phổ biến hơn.


dynamic_cast hầu như chỉ được sử dụng để xử lý đa hình. Bạn có thể đúc một con trỏ hoặc tham chiếu đến bất kỳ loại đa hình nào cho bất kỳ loại lớp nào khác (một loại đa hình có ít nhất một hàm ảo, được khai báo hoặc được kế thừa). Bạn có thể sử dụng nó nhiều hơn là chỉ truyền xuống - bạn có thể truyền sang một bên hoặc thậm chí lên một chuỗi khác. Các dynamic_cast sẽ tìm ra đối tượng mong muốn và trả lại nó nếu có thể. Nếu nó không thể, nó sẽ trở lại nullptr trong trường hợp của một con trỏ, hoặc ném std::bad_cast trong trường hợp tham chiếu.

dynamic_cast có một số hạn chế, mặc dù. Nó không hoạt động nếu có nhiều đối tượng cùng loại trong hệ thống phân cấp thừa kế (cái gọi là 'kim cương đáng sợ') và bạn không sử dụng virtual di sản. Nó cũng chỉ có thể đi qua thừa kế công cộng - nó sẽ luôn thất bại trong việc đi qua protected hoặc là private di sản. Điều này hiếm khi là một vấn đề, tuy nhiên, như các hình thức thừa kế như vậy là rất hiếm.


reinterpret_cast là diễn viên nguy hiểm nhất, và nên được sử dụng rất ít. Nó biến một loại trực tiếp thành một loại khác - chẳng hạn như truyền giá trị từ con trỏ này sang con trỏ khác hoặc lưu trữ con trỏ trong một inthoặc tất cả những thứ khó chịu khác. Phần lớn, đảm bảo duy nhất bạn nhận được reinterpret_cast là bình thường nếu bạn đưa kết quả trở lại kiểu gốc, bạn sẽ nhận được cùng một giá trị giống nhau (nhưng không phải nếu loại trung gian nhỏ hơn loại gốc). Có một số chuyển đổi reinterpret_cast cũng không thể làm được. Nó được sử dụng chủ yếu cho các chuyển đổi đặc biệt lạ và thao tác bit, như chuyển luồng dữ liệu thô thành dữ liệu thực hoặc lưu trữ dữ liệu trong các bit thấp của một con trỏ được căn chỉnh.


Dàn diễn viên kiểu C và dàn diễn viên chức năng là phôi sử dụng (type)object hoặc là type(object), tương ứng. Một dàn diễn viên kiểu C được định nghĩa là lần đầu tiên sau đây thành công:

  • const_cast
  • static_cast (mặc dù bỏ qua các giới hạn truy cập)
  • static_cast (xem ở trên), sau đó const_cast
  • reinterpret_cast
  • reinterpret_cast, sau đó const_cast

Do đó, nó có thể được sử dụng như một sự thay thế cho các phôi khác trong một số trường hợp, nhưng có thể cực kỳ nguy hiểm vì khả năng biến thành một reinterpret_castvà sau này nên được ưu tiên khi cần truyền rõ ràng, trừ khi bạn chắc chắn static_cast sẽ thành công hoặc reinterpret_cast sẽ thất bại. Thậm chí sau đó, hãy xem xét tùy chọn dài hơn, rõ ràng hơn.

C-style phôi cũng bỏ qua kiểm soát truy cập khi thực hiện một static_cast, có nghĩa là họ có khả năng thực hiện một hoạt động mà không có diễn viên nào khác có thể. Điều này chủ yếu là một kludge, mặc dù, và trong tâm trí của tôi chỉ là một lý do khác để tránh phôi kiểu C.


2211
2017-12-01 20:22



dynamic_cast chỉ dành cho các loại đa hình. bạn chỉ cần sử dụng nó khi bạn đang truyền đến một lớp dẫn xuất. static_cast chắc chắn là tùy chọn đầu tiên trừ khi bạn đặc biệt cần functinoality của dynamic_cast. Nó không phải là một số “viên đúc kiểm tra loại” bạc kỳ diệu nói chung. - jalf
Câu trả lời chính xác! Một nhận xét nhanh: static_cast có thể cần thiết để phân cấp cấu trúc phân cấp trong trường hợp bạn có Derived * & cast thành Base * &, vì hai con trỏ / tham chiếu không tự động bỏ phân cấp. Tôi đã tình cờ gặp tình huống này (cách thẳng thắn, không phổ biến) cách đây hai phút. ;-) - bartgol
* "không có trình diễn C ++ nào khác có khả năng xóa const (thậm chí không reinterpret_cast) "... thật sao? Còn về reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))? - Mehrdad
Tôi nghĩ rằng một chi tiết quan trọng còn thiếu ở trên là dynamic_cast có một hiệu suất thời gian chạy hình phạt so với tĩnh hoặc reinterpret_cast. Điều này quan trọng, ví dụ: trong phần mềm thời gian thực. - jfritz42
Có thể đáng nói đến reinterpret_cast thường là vũ khí được lựa chọn khi xử lý tập dữ liệu mờ của API - camelCase


Sử dụng dynamic_cast để chuyển đổi con trỏ / tham chiếu trong một hệ thống phân cấp thừa kế.

Sử dụng static_cast cho các chuyển đổi loại thông thường.

Sử dụng reinterpret_cast cho việc tái diễn ở mức độ thấp của các mẫu bit. Sử dụng hết sức thận trọng.

Sử dụng const_cast để bỏ đi const/volatile. Tránh điều này trừ khi bạn đang mắc kẹt bằng cách sử dụng một API không chính xác.


284
2018-01-21 04:53





(Rất nhiều giải thích lý thuyết và khái niệm đã được đưa ra ở trên) 

Dưới đây là một số ví dụ thực tế khi tôi sử dụng static_cast, dynamic_cast, const_cast, reinterpret_cast.

(Cũng tham khảo điều này để hiểu giải thích: http://www.cplusplus.com/doc/tutorial/typecasting/)

static_cast:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}

152
2017-12-11 02:05



Lý thuyết của một số câu trả lời khác là tốt, nhưng vẫn còn khó hiểu, nhìn thấy những ví dụ sau khi đọc các câu trả lời khác thực sự làm cho tất cả chúng có ý nghĩa. Đó là không có các ví dụ, tôi vẫn không chắc chắn, nhưng với họ, tôi bây giờ chắc chắn về những gì các câu trả lời khác có ý nghĩa. - Solx
Về việc sử dụng lần cuối của reinterpret_cast: không phải điều này giống như sử dụng static_cast<char*>(&val) ? - Lorenzo Belli
@LorenzoBelli Tất nhiên là không. Bạn đã thử à? Sau này không hợp lệ C ++ và khối biên dịch. static_cast chỉ hoạt động giữa các loại có chuyển đổi được xác định, mối quan hệ hiển thị của kế thừa hoặc đến / từ void *. Đối với mọi thứ khác, có các phôi khác. reinterpret cast cho bất kỳ char * loại được phép cho phép đọc biểu diễn của bất kỳ đối tượng nào - và một trong những trường hợp duy nhất mà từ khóa đó hữu ích, không phải là một trình tạo tràn lan của hành vi thực hiện / không xác định. Nhưng điều này không được coi là một chuyển đổi 'bình thường', do đó, không được phép (thường) rất bảo thủ static_cast. - underscore_d
reinterpret_cast khá phổ biến khi bạn đang làm việc với phần mềm hệ thống như cơ sở dữ liệu. Hầu hết các trường hợp bạn viết trình quản lý trang của riêng bạn mà không có ý tưởng về loại dữ liệu được lưu trữ trong trang và chỉ trả về một con trỏ void. Của nó lên đến các cấp cao hơn để làm một diễn viên diễn giải lại và suy ra nó như bất cứ điều gì họ muốn. - Sohaib


Nó có thể giúp ích nếu bạn biết một chút về nội bộ ...

static_cast

  • Trình biên dịch C ++ đã biết cách chuyển đổi các loại scaler như float thành int. Sử dụng static_cast cho họ.
  • Nói chung, trong khi chuyển đổi loại A sang B, static_cast sẽ gọi hàm tạo của B đi qua nó A. Nếu B không có hàm tạo như vậy thì bạn sẽ nhận được lỗi thời gian biên dịch.
  • Truyền từ A* đến B* luôn thành công nếu A và B nằm trong hệ thống phân cấp thừa kế (hoặc void) nếu không bạn sẽ nhận được lỗi biên dịch.
  • Gotcha: Nếu bạn cast con trỏ cơ sở để con trỏ có nguồn gốc nhưng nếu đối tượng thực tế là nếu không có nguồn gốc loại thì bạn không gặp lỗi. Bạn nhận được con trỏ xấu và ngay sau khi bạn cố gắng truy cập các thành viên của con trỏ có nguồn gốc, bạn nhận được segfault lúc chạy.
  • Cùng đi cho A& đến B&.
  • Gotcha: Truyền từ Derived to Base hoặc viceversa tạo bản sao mới! Đối với những người đến từ C # / Java, nhiều người ở trên có thể là một bất ngờ lớn.

dynamic_cast

  • dynamic_cast sử dụng thông tin loại thời gian chạy để tìm hiểu xem dàn diễn viên có hợp lệ hay không. Ví dụ, (Base*) đến (Derived*)có thể thất bại nếu con trỏ không thực sự thuộc loại có nguồn gốc.
  • Điều này có nghĩa là dynamic_cast rất đắt so với static_cast!
  • Dành cho A* đến B*, nếu cast không hợp lệ thì dynamic_cast sẽ trả về nullptr.
  • Dành cho A& đến B& nếu cast không hợp lệ thì dynamic_cast sẽ ném bad_cast exception.
  • Không giống như các phôi khác, có thời gian chạy trên cao.

const_cast

  • Trong khi static_cast có thể làm không const để const nó không thể đi theo cách khác xung quanh. Các const_cast có thể làm cả hai cách.
  • Một ví dụ mà điều này có ích là lặp qua một số vùng chứa như set<T> chỉ trả về các phần tử của nó làm const để đảm bảo bạn không thay đổi khóa của nó. Tuy nhiên, nếu ý định của bạn là sửa đổi các thành viên không phải là chìa khóa của đối tượng thì nó sẽ ổn. Bạn có thể sử dụng const_cast để loại bỏ constness.
  • Một ví dụ khác là khi bạn muốn triển khai T& foo() cũng như const T& foo(). Để tránh trùng lặp mã, bạn có thể áp dụng const_cast để trả về giá trị của một hàm từ một hàm khác.

reinterpret_cast

  • Điều này về cơ bản nói rằng có những byte tại vị trí bộ nhớ này và nghĩ về nó như là đối tượng nhất định.
  • Ví dụ, bạn có thể tải 4 byte của float đến 4 byte int để xem bit trong float trông như thế nào.
  • Rõ ràng, nếu dữ liệu không chính xác cho loại, bạn có thể nhận được segfault.
  • Không có chi phí thời gian chạy cho dàn diễn viên này.

52
2017-12-01 20:20



Điểm cuối cùng về const_cast không chính xác: chỉ dynamic_cast thực sự có chi phí thời gian chạy. Nó có vẻ như bạn có thể đã chuyển thứ tự và quên di chuyển và reword đó. - rubenvb
Bạn là chính xác! Đã sửa. - ShitalShah


Làm điều này trả lời câu hỏi của bạn?

Tôi chưa bao giờ sử dụng reinterpret_cast, và tự hỏi liệu có chạy vào một trường hợp cần thiết không phải là mùi của thiết kế tồi. Trong cơ sở mã tôi làm việc trên dynamic_cast được sử dụng rất nhiều. Sự khác biệt với static_cast Đó là dynamic_cast kiểm tra thời gian chạy có thể (an toàn hơn) hoặc có thể không (chi phí cao hơn) là những gì bạn muốn (xem msdn).


11
2018-05-31 14:16



Tôi đã sử dụng reintrepret_cast cho một mục đích - nhận được các bit ra khỏi một đôi (cùng kích thước dài trên nền tảng của tôi). - Joshua
reinterpret_cast là cần thiết, ví dụ: để làm việc với các đối tượng COM. CoCreateInstance () có thông số đầu ra của loại void ** (tham số cuối cùng), trong đó bạn sẽ chuyển con trỏ của bạn được khai báo là ví dụ: "INetFwPolicy2 * pNetFwPolicy2". Để làm điều đó, bạn cần phải viết một cái gì đó như reinterpret_cast <void **> (& pNetFwPolicy2). - Serge Rogatch


Ngoài các câu trả lời khác cho đến nay, đây là ví dụ không rõ ràng ở đâu static_cast không đủ để reinterpret_cast là cần thiết. Giả sử có một hàm trong một tham số đầu ra trả về con trỏ tới các đối tượng của các lớp khác nhau (mà không chia sẻ một lớp cơ sở chung). Một ví dụ thực sự về chức năng như vậy là CoCreateInstance() (xem tham số cuối cùng, thực tế là void**). Giả sử bạn yêu cầu lớp đối tượng cụ thể từ hàm này, vì vậy bạn biết trước loại cho con trỏ (mà bạn thường làm cho các đối tượng COM). Trong trường hợp này, bạn không thể đưa con trỏ tới con trỏ của mình void** với static_cast: bạn cần reinterpret_cast<void**>(&yourPointer).

Trong mã:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

Tuy nhiên, static_cast hoạt động cho các con trỏ đơn giản (không phải con trỏ đến con trỏ), vì vậy mã trên có thể được viết lại để tránh reinterpret_cast (với giá của một biến phụ) theo cách sau:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);

9