2010-03-04 27 views
11

AFAIK, đối với con trỏ/tham chiếu static_cast, nếu định nghĩa lớp không hiển thị cho trình biên dịch tại thời điểm này, thì static_cast sẽ hoạt động như reinterpret_cast.static_cast safety

Tại sao static_cast không an toàn cho con trỏ/tham chiếu và an toàn cho các giá trị số?

Trả lời

22

Tóm lại, vì đa thừa kế.

Trong dài:

#include <iostream> 

struct A { int a; }; 
struct B { int b; }; 
struct C : A, B { int c; }; 

int main() { 
    C c; 
    std::cout << "C is at : " << (void*)(&c) << "\n"; 
    std::cout << "B is at : " << (void*)static_cast<B*>(&c) << "\n"; 
    std::cout << "A is at : " << (void*)static_cast<A*>(&c) << "\n"; 

} 

Output:

C is at : 0x22ccd0 
B is at : 0x22ccd4 
A is at : 0x22ccd0 

Lưu ý rằng để chuyển đổi một cách chính xác đến B *, static_cast có thay đổi giá trị con trỏ. Nếu trình biên dịch không có định nghĩa lớp cho C, thì nó sẽ không biết rằng B là một lớp cơ sở, và chắc chắn nó sẽ không biết những gì bù đắp để áp dụng.

Nhưng trong tình huống đó mà không có định nghĩa là có thể nhìn thấy, static_cast không cư xử như reinterpret_cast, nó bị cấm:

struct D; 
struct E; 

int main() { 
    E *p1 = 0; 
    D *p2 = static_cast<D*>(p1); // doesn't compile 
    D *p3 = reinterpret_cast<D*>(p1); // compiles, but isn't very useful 
} 

Một dàn diễn viên đồng bằng C-phong cách, (B*)(&c) làm những gì bạn nói: nếu định nghĩa của struct C có thể nhìn thấy, cho thấy rằng B là một lớp cơ sở, sau đó nó giống như một static_cast. Nếu các kiểu chỉ được khai báo về phía trước, thì nó sẽ giống như một kiểu reinterpret_cast. Điều này là do nó được thiết kế tương thích với C, có nghĩa là nó phải làm những gì C thực hiện trong trường hợp có thể trong C.

static_cast luôn biết phải làm gì với các kiểu tích hợp sẵn, đó thực sự là những gì được tích hợp sẵn có nghĩa. Nó có thể chuyển đổi int sang phao, v.v. Vì vậy, đó là lý do tại sao nó luôn an toàn cho các loại số, nhưng nó không thể chuyển đổi con trỏ trừ khi (a) nó biết những gì họ trỏ đến, và (b) có loại mối quan hệ đúng giữa các loại được chỉ định. Do đó, nó có thể chuyển đổi int thành float, nhưng không phải int* thành float*.

Như AndreyT nói, có một cách mà bạn có thể sử dụng static_cast không an toàn, và trình biên dịch có thể sẽ không giúp bạn tiết kiệm, vì mã là hợp pháp:

A a; 
C *cp = static_cast<C*>(&a); // compiles, undefined behaviour 

Một trong những điều có thể làm là static_cast "downcast" một con trỏ đến một lớp dẫn xuất (trong trường hợp này, C là một lớp dẫn xuất của A). Nhưng nếu referand không thực sự thuộc lớp dẫn xuất, bạn sẽ phải chịu số phận. A dynamic_cast sẽ thực hiện kiểm tra trong thời gian chạy, nhưng đối với lớp C ví dụ của tôi, bạn không thể sử dụng dynamic_cast, vì A không có chức năng ảo.

Bạn cũng có thể làm những việc không an toàn tương tự với static_cast đến và đi từ void*.

+0

Nhiều kế thừa không phải là vấn đề duy nhất, câu trả lời của AndreyT giải quyết vấn đề downcasting đến sai loại. –

+0

Đúng, tôi chỉ trả lời đầy đủ đoạn đầu của câu hỏi, và một phần đoạn thứ hai. Không phải là vấn đề lớn (trong tiêu đề) của an toàn tĩnh. –

5

Không, "AFAIK" của bạn không chính xác. static_cast không bao giờ hoạt động như reinterpret_cast (ngoại trừ, có thể khi bạn chuyển đổi thành void *, mặc dù chuyển đổi này thường không được thực hiện bởi reinterpret_cast).

Thứ nhất, khi static_cast được sử dụng cho con trỏ hoặc chuyển đổi tham chiếu, đặc điểm kỹ thuật của static_cast yêu cầu một mối quan hệ nhất định phải tồn tại giữa các loại (và được biết đến static_cast).Đối với loại lớp, chúng phải liên quan đến thừa kế, như được nhận biết bởi static_cast. Không thể đáp ứng yêu cầu đó mà không có cả hai loại được xác định hoàn toàn theo điểm static_cast. Vì vậy, nếu định nghĩa (s) là (là) không thể nhìn thấy tại các điểm của static_cast, mã đơn giản sẽ không biên dịch.

Để minh họa ở trên với các ví dụ: static_cast có thể được sử dụng [redundantly] để thực hiện upcasts con trỏ đối tượng. Mã

Derived *derived = /* whatever */; 
Base *base = static_cast<Base *>(derived); 

chỉ compilable khi mã sau là compilable

Base *base(derived); 

và cho điều này để biên dịch định nghĩa của cả hai loại phải được hiển thị.

Ngoài ra, static_cast có thể được sử dụng để thực hiện các lần xóa con trỏ đối tượng. Mã

Base *base = /* whatever */; 
Derived *derived = static_cast<Derived *>(base); 

chỉ compilable khi mã sau là compilable

Base *base(derived); // reverse direction 

và, một lần nữa, cho điều này để biên dịch định nghĩa của cả hai loại phải được hiển thị.

Vì vậy, bạn chỉ đơn giản là không thể sử dụng static_cast với các loại không xác định. Nếu trình biên dịch của bạn cho phép, đó là một lỗi trong trình biên dịch của bạn.

static_cast có thể không an toàn cho con trỏ/tham chiếu vì một lý do hoàn toàn khác. static_cast có thể thực hiện các chương trình giảm cấp bậc cho các kiểu đối tượng/con trỏ mà không kiểm tra loại động thực tế của đối tượng. static_cast cũng có thể thực hiện upcasts phân cấp cho các loại con trỏ phương thức. Sử dụng kết quả của các phôi được bỏ chọn này có thể dẫn đến hành vi không xác định, nếu được thực hiện mà không cần thận trọng.

Thứ hai, khi static_cast được sử dụng với các loại số học, ngữ nghĩa là hoàn toàn khác nhau và không liên quan gì đến điều trên. Nó chỉ thực hiện chuyển đổi loại số học. Họ luôn luôn hoàn toàn an toàn (ngoài hình thức các vấn đề phạm vi), miễn là họ phù hợp với ý định của bạn. Trên thực tế, nó có thể là một kiểu lập trình tốt để tránh static_cast cho chuyển đổi số học và sử dụng phôi kiểu C thay thế, chỉ để cung cấp sự phân biệt rõ ràng trong mã nguồn giữa phôi số học luôn an toàn và phôi trỏ/tham chiếu phân cấp có khả năng không an toàn .

+0

"sử dụng cũ C_style phôi thay vì" - đó là những gì tôi thường làm, nhưng sau đó ai đó than phiền và chúng tôi kết luận lập luận cho dù có bất kỳ sự khác biệt giữa một C-phong cách diễn viên mà họ nói là ác, và một đối số đơn constructor rằng họ ' lại hoàn toàn hài lòng với ;-) –

+0

vì vậy, nếu static_cast không biên dịch cho con trỏ không xác định/tham chiếu, nó có thể được coi là an toàn cho loại chuyển đổi đó? – dimba

+0

@idimba: Er ... Tôi có thời gian khó cố gắng áp dụng các khái niệm "an toàn" và "không an toàn" cho mã không biên dịch. Tôi thậm chí còn nói rằng nó có ý nghĩa rất ít. – AnT

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