2013-02-19 65 views
9

Có thể downcast một đối tượng vào một phân lớp không định nghĩa bất kỳ biến phụ hay phương thức ảo nào không?Có thể downcast một đối tượng vào một lớp con không định nghĩa biến phụ hay vtable trong C++?

Nếu tôi có các lớp này,

class A { public: A(); }; 
class B : public A { public: void method1() {} B(); }; 

là thế này (1) có thể và (2) an toàn theo tiêu chuẩn?

A* a = new A(); 
B* b = (B*)a; 
b->method1(); 
+7

Không, 'A' không phải là' B'. –

+0

Câu hỏi của bạn không khớp với những gì bạn đang yêu cầu. Câu hỏi của bạn hỏi về việc truyền các đối tượng, trong khi mã của bạn đang truyền con trỏ. – Mehrdad

+1

Liên quan: http://stackoverflow.com/questions/11404209/c-memory-layout-of-inheritance – jogojapan

Trả lời

6

Chuyển đổi con trỏ đóng vai trò là static_cast. 5.2.9/2 cho biết,

Nếu đối tượng của loại [A] thực sự là một subobject của một đối tượng của loại [B], kết quả đề cập đến đối tượng kèm theo các loại [B]. Nếu không, kết quả của dàn diễn viên sẽ không được xác định.

Đối tượng của bạn là không một subobject của một đối tượng B, vì vậy kết quả là không xác định.

Ngay cả khi bạn đã đến reinterpret_cast, việc truy cập giá trị của đối tượng thông qua con trỏ kết quả có hành vi không xác định do vi phạm bí danh nghiêm ngặt. Trong tiêu chuẩn C++ 11, 3.10/10:

Nếu một chương trình cố gắng truy cập vào các giá trị được lưu trữ của một đối tượng thông qua một glvalue của khác hơn là một trong các loại sau đây hành vi này là fi unde định nghĩa:

"Một lớp học có nguồn gốc của loại động của đối tượng không thêm thành viên dữ liệu hoặc chức năng thành viên ảo "là không phải là trong danh sách sau.

Tùy thuộc vào những gì method1 thực sự làm, có thể tránh truy cập vào giá trị được lưu trữ của đối tượng khi gọi. Nhưng tôi không chắc nó có thể làm được không. Trừ khi được nêu khác đi trong tiêu chuẩn, tôi sẽ giả định cho sự an toàn gọi hàm thành viên không tĩnh vốn "truy cập giá trị được lưu trữ của đối tượng" ngay cả khi chức năng không thực sự sử dụng bất kỳ thành viên dữ liệu nào.

Đây là một trong những trường hợp khó xử đó có thể sẽ hoạt động trong thực tế hoặc là mọi lúc hoặc gần như mọi lúc. Nhưng nó không được bảo đảm, do đó, ngay cả khi nó xuất hiện để làm việc và mã phát ra có vẻ OK, bạn sẽ sống trong sợ hãi rằng một ngày nào đó một tối ưu hóa mới sẽ phá vỡ nó.

Sau khi được xác định, các lớp C++ được đóng cho các thành viên mới, bao gồm các hàm thành viên mới. Vì vậy, đối tượng của bạn tạo ra với new A() có tất cả các chức năng thành viên nó sẽ bao giờ có. Chỉ cần viết một chức năng không phải là thành viên - trừ khi Aprotected thành viên, nó sẽ có quyền truy cập chính xác vào A mà chức năng thành viên của bạn có. Và nếu A không có protected thành viên thì có được phê duyệt cách lấy từ nó, mà bạn nên sử dụng để tạo các phiên bản thích hợp B.

Nếu cú ​​pháp hàm thành viên có nghĩa là nhiều cho bạn, sau đó tùy thuộc vào lớp A bạn có thể có thể viết:

B b = *a; // "copy" the object (give B a suitable ctor) 
b.method1(); // act on it 
*a = b;  // copy it back (A needs copy assignment operator) 

Rõ ràng là có vấn đề ở đây là có thể ngăn chặn nó làm việc: để bắt đầu với liệu đối tượng là có thể sao chép, cũng an toàn chủ đề và liệu method1 lưu trữ một con trỏ/tham chiếu đến b ở đâu đó sẽ bắt đầu dangle ngay sau khi b bị hủy. Trong C++ 11 các bản sao có lẽ có thể di chuyển hiệu quả, nhưng thậm chí vì vậy tôi hy vọng bạn sẽ đồng ý rằng các vòng bạn phải nhảy qua để sử dụng cú pháp hàm thành viên không đáng giá.

+2

Về mã cuối cùng của bạn, một cách thuận tiện hơn có thể là làm cho 'B' một trình bao quanh' A': thay vì sao chép đối tượng thành 'b',' b' sẽ giữ một tham chiếu/con trỏ tới 'a', và hành động trên đó. Đây là một mô hình khá phổ biến. –

+0

thực sự không thể có vấn đề với việc chuyển đổi con trỏ miễn là lớp dẫn xuất không có thành viên dữ liệu. –

+0

@ErikAronesty: bạn chắc chắn nên đăng câu trả lời trích dẫn nơi tiêu chuẩn đảm bảo rằng, nếu có. Nếu câu trả lời của tôi sai thì tôi không thể xóa câu trả lời vì nó được chấp nhận, nhưng tôi có thể nhắc đến câu trả lời của bạn. –

1

Bạn nên đọc bài rất tốt bằng văn bản này:

Regular cast vs. static_cast vs. dynamic_cast

Nếu bạn sử dụng "static_cast", được cảnh báo rằng bạn đang "ép" chuyển đổi và điều này vốn đã không an toàn.

+0

Và anh ta không thể sử dụng 'dynamic_cast', bởi vì không có chức năng ảo. –

3

B * b = static_cast < B *> a hoặc những gì bạn đã thử là không an toàn downcasting, nó gán địa chỉ của đối tượng lớp cơ sở (A) cho con trỏ có lớp (B). Vì vậy, nếu bạn truy cập bất cứ điều gì thông qua con trỏ đó, nó sẽ gây ra hành vi không xác định.

Giả sử một bố trí đối tượng có thể có của một thể hiện của A:

a ->|vptr| 

Khi bạn thực hiện một dàn diễn viên buộc:

b ->|vptr| 

nó chắc chắn là không an toàn vì một không trỏ đến một thể hiện của B hoặc phân lớp của B. Khi bạn gọi một phương pháp ảo hoặc sửa đổi một trường (không phải trong trường hợp này), nó sẽ gây ra undefined behavior nói chung hoặc lỗi trong bố cục này.

Tuy nhiên, phương thức 1 của bạn không phải là ảo, vì vậy không cần tra cứu bảng ảo. Kể từ khi bạn thực hiện method1 không và thậm chí không thể làm bất cứ điều gì về "này", Vì vậy, khi bạn chạy mã trong bố trí đối tượng giả thuyết này, nó sẽ probabaly báo cáo không có lỗi (Xem bình luận của James).

+0

Hmm. Từ bố cục mẫu của bạn, có vẻ như chúng thực sự giống hệt nhau. Cũng lưu ý rằng trong đoạn mã do OP đưa ra, không có chức năng ảo nào liên quan. – jogojapan

+0

Các phương thức không được lưu trữ trong các đối tượng, không có sự bù đắp liên quan đến việc gọi hàm thành viên (trừ khi nó là ảo, trong trường hợp đó hàm được lấy ra từ bảng phương thức ảo, cũng không được lưu trữ trong đối tượng). –

+0

Cảm ơn, chỉ cần nhận thấy rằng method1 là không ảo. – StarPinkER

3
  1. Có, điều đó là có thể.
  2. Không, không an toàn theo tiêu chuẩn; nó không được khuyến khích.

Điều này giống C void * casting hơn, đây không phải là điều tốt nhất để thực hiện trong C++. Nhưng tôi đã làm điều này nhiều lần và nó hoạt động tốt.

+0

có rất nhiều công dụng tuyệt vời cho downcasting. như xác định "không gian làm việc" và sau đó xác định "thuật toán" có nguồn gốc không có thành viên dữ liệu nào hoạt động trên không gian làm việc đó. Các thuật toán được thực hiện tốt hơn như là nguồn gốc ... nhưng các con trỏ từ một thuật toán luôn có thể được đúc an toàn đến cơ sở. hãy tưởng tượng một nhà máy sản xuất chúng ... cơ sở không cần biết vtable là gì. tất cả những gì nó làm là lặp lại và thực thi. –

+0

@ErikAronesty, đối số của bạn là quan trọng. Nó khác với bất kỳ ai khác, và tôi thấy những gì bạn nói xảy ra trong sản phẩm thế giới thực. Bạn có thể vui lòng giải thích thêm một chút không? I E. nó KHÔNG an toàn. – solotim

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