2009-10-27 34 views
29

Tôi cơ bản tự hỏi làm thế nào C++ đưa ra đối tượng trong bộ nhớ. Vì vậy, tôi nghe rằng phôi năng động chỉ đơn giản là điều chỉnh con trỏ của đối tượng trong bộ nhớ với một bù đắp; và loại diễn giải lại cho phép chúng ta làm bất cứ điều gì với con trỏ này. Tôi không thực sự hiểu điều này. Chi tiết sẽ được đánh giá cao!Bố cục bộ nhớ Đối tượng C++

Trả lời

8

Mỗi lớp đưa ra các thành viên dữ liệu của nó theo thứ tự khai báo.
Trình biên dịch được phép đặt đệm giữa các thành viên để truy cập hiệu quả (nhưng không được phép sắp xếp lại).

Cách hoạt động của dynamic_cast<> là chi tiết triển khai trình biên dịch và không được xác định theo tiêu chuẩn. Tất cả sẽ phụ thuộc vào ABI được trình biên dịch sử dụng.

reinterpret_cast<> hoạt động bằng cách chỉ thay đổi loại đối tượng. Điều duy nhất mà bạn có thể đảm bảo rằng các công trình là đúc một con trỏ đến một void * và trở lại cùng một con trỏ đến lớp sẽ cung cấp cho bạn cùng một con trỏ.

+3

điểm đầu tiên của bạn là không hoàn toàn chính xác. Sự bảo đảm duy nhất bạn có là các thành viên trong cùng một khối truy cập sẽ có một thứ tự xác định. Nếu bạn muốn thực hiện điều này đến mức cực đoan, bạn có thể nói rằng ngay cả khi quyền truy cập giống với thứ tự không còn được đảm bảo. –

+0

@Richard. Tôi không chắc tôi hiểu bạn. Trình biên dịch không được phép sắp xếp lại các phần tử (điều này là dành cho khả năng tương thích của các phần sau với C). Khối truy cập là gì. Bạn có thể chỉ cho tôi vào phần chính xác của tiêu chuẩn mà bạn đang nhận được thông tin của bạn không? –

+2

Một khối truy cập (thực sự, access specifier) ​​là 'public:', 'private:', và 'protected:' trong một lớp. Tôi tìm thấy bài viết này: http://www.embedded.com/design/218600150?pgno=1 cực kỳ hữu ích, và đó là nơi tôi lần đầu tiên học được những gì Richard đã đề cập trong bình luận của mình. Tôi tra cứu tiêu chuẩn C++ được liên kết ở đó, và trên pg. 198 (điều 9.2 khoản 12), tiểu bang: "Thứ tự phân bổ các thành viên dữ liệu không tĩnh với điều khiển truy cập khác không được chỉ định" –

4

Câu trả lời là "phức tạp". Dynamic cast không chỉ đơn giản là điều chỉnh con trỏ với một bù đắp; nó thực sự có thể lấy các con trỏ bên trong bên trong đối tượng để thực hiện công việc của nó. GCC tuân theo ABI được thiết kế cho Itanium nhưng được triển khai rộng rãi hơn. Bạn có thể tìm thấy chi tiết đẫm máu ở đây: Itanium C++ ABI.

4

Như đã nêu trước đây, các chi tiết đầy đủ phức tạp, khó đọc và thực sự chỉ hữu ích cho các nhà phát triển trình biên dịch và khác nhau giữa các trình biên dịch. Về cơ bản, mỗi đối tượng chứa sau (thường đặt ra theo thứ tự này):

  1. loại Runtime thông tin
  2. đối tượng cơ sở phi ảo và dữ liệu của họ (có lẽ theo thứ tự khai).
  3. Biến thành viên
  4. Đối tượng cơ sở ảo và dữ liệu của chúng (Có thể trong một số thứ tự tìm kiếm của DFS).

Những phần dữ liệu này có thể hoặc không được đệm để làm cho liên kết bộ nhớ dễ dàng hơn ... Ẩn trong thông tin kiểu thời gian chạy là thông tin về loại, v-bảng cho các lớp cha mẹ v.v ..., tất cả đều là trình biên dịch cụ thể .

Khi nói đến phôi, reinterpret_cast chỉ đơn giản là thay đổi loại dữ liệu C++ của con trỏ và không làm gì khác, vì vậy bạn nên chắc chắn rằng bạn biết mình đang làm gì khi sử dụng, nếu không bạn sẽ bị nhầm lẫn mọi thứ trở nên tồi tệ. dynamic_cast thực hiện rất nhiều điều tương tự như static_cast (trong việc thay đổi con trỏ), ngoại trừ nó sử dụng thông tin kiểu thời gian chạy để tìm hiểu xem nó có thể truyền đến kiểu đã cho hay không và cách thực hiện. Một lần nữa, tất cả đó là trình biên dịch cụ thể. Lưu ý rằng bạn không thể dynamic_cast một void* vì nó cần biết nơi để tìm thông tin loại thời gian chạy để có thể thực hiện tất cả các kiểm tra thời gian chạy tuyệt vời của nó.

1

câu hỏi này đã được trả lời tại http://dieharddeveloper.blogspot.in/2013/07/c-memory-layout-and-process-image.html đây là một đoạn trích từ đó: Ở giữa không gian địa chỉ của quá trình, có một khu vực được dành riêng cho các đối tượng chia sẻ. Khi một quy trình mới được tạo ra, trình quản lý quy trình đầu tiên ánh xạ hai phân đoạn từ tệp thực thi vào bộ nhớ. Sau đó nó giải mã tiêu đề ELF của chương trình. Nếu tiêu đề chương trình chỉ ra rằng tệp thực thi được liên kết với một thư viện được chia sẻ, trình quản lý quy trình (PM) sẽ trích xuất tên của trình thông dịch động từ tiêu đề chương trình. Trình thông dịch động trỏ tới một thư viện được chia sẻ có chứa mã trình liên kết thời gian chạy.

+1

Trong khi đó là thông tin giá trị, nó trả lời một câu hỏi khác. – domen

9

Bố cục bộ nhớ hầu như được để thực hiện. Ngoại lệ chính là các biến thành viên cho một trình chỉ định truy cập cụ thể sẽ theo thứ tự khai báo của chúng.

§ 9.2.14

không tĩnh dữ liệu thành viên của một (phi đoàn) lớp với cùng truy cập điều khiển (khoản 11) được phân bổ để các thành viên sau có cao hơn địa chỉ trong vòng một đối tượng lớp. Thứ tự phân bổ không tĩnh thành viên dữ liệu có kiểm soát truy cập khác nhau không được chỉ định (11). Yêu cầu căn chỉnh thực hiện có thể khiến hai thành viên liền kề không được phân bổ ngay sau mỗi khác; có thể yêu cầu không gian để quản lý các chức năng ảo (10.3) và các lớp cơ sở ảo (10.1).

Khác với biến thành viên, lớp hoặc cấu trúc cần cung cấp không gian cho biến thành viên, lớp con của lớp cơ sở, quản lý chức năng ảo (ví dụ: bảng ảo) và đệm và căn chỉnh các dữ liệu này. Điều này phụ thuộc vào việc thực hiện nhưng đặc tả Itanium ABI là một lựa chọn phổ biến. gcc và clang tuân thủ nó (ít nhất là một mức độ).

http://mentorembedded.github.io/cxx-abi/abi.html#layout

Các Itanium ABI là tất nhiên không nằm trong chuẩn C++ và không ràng buộc. Để biết chi tiết hơn, bạn cần phải chuyển sang tài liệu và công cụ của người triển khai. clang cung cấp một công cụ để xem bố cục bộ nhớ của các lớp. Ví dụ: như sau:

class VBase { 
    virtual void corge(); 
    int j; 
}; 

class SBase1 { 
    virtual void grault(); 
    int k; 
}; 

class SBase2 { 
    virtual void grault(); 
    int k; 
}; 

class SBase3 { 
    void grault(); 
    int k; 
}; 

class Class : public SBase1, SBase2, SBase3, virtual VBase { 
public: 
    void bar(); 
    virtual void baz(); 
    // virtual member function templates not allowed, thinking about memory 
    // layout and vtables will tell you why 
    // template<typename T> 
    // virtual void quux(); 
private: 
    int i; 
    char c; 
public: 
    float f; 
private: 
    double d; 
public: 
    short s; 
}; 

class Derived : public Class { 
    virtual void qux(); 
}; 

int main() { 
    return sizeof(Derived); 
} 

Sau khi tạo tệp nguồn sử dụng bố cục bộ nhớ của lớp, clang sẽ hiển thị bố cục bộ nhớ.

$ clang -cc1 -fdump-record-layouts layout.cpp 

Việc bố trí cho Class:

*** Dumping AST Record Layout 
    0 | class Class 
    0 | class SBase1 (primary base) 
    0 |  (SBase1 vtable pointer) 
    8 |  int k 
    16 | class SBase2 (base) 
    16 |  (SBase2 vtable pointer) 
    24 |  int k 
    28 | class SBase3 (base) 
    28 |  int k 
    32 | int i 
    36 | char c 
    40 | float f 
    48 | double d 
    56 | short s 
    64 | class VBase (virtual base) 
    64 |  (VBase vtable pointer) 
    72 |  int j 
    | [sizeof=80, dsize=76, align=8 
    | nvsize=58, nvalign=8] 

Thông tin thêm về tính năng kêu vang này có thể được tìm thấy trên Eli Bendersky blog:

http://eli.thegreenplace.net/2012/12/17/dumping-a-c-objects-memory-layout-with-clang/

gcc cung cấp một công cụ tương tự, `-fdump -class-hierarchy '. Đối với các lớp học được nêu ở trên, nó in (trong số những thứ khác):

Class Class 
    size=80 align=8 
    base size=58 base align=8 
Class (0x0x141f81280) 0 
    vptridx=0u vptr=((& Class::_ZTV5Class) + 24u) 
    SBase1 (0x0x141f78840) 0 
     primary-for Class (0x0x141f81280) 
    SBase2 (0x0x141f788a0) 16 
     vptr=((& Class::_ZTV5Class) + 56u) 
    SBase3 (0x0x141f78900) 28 
    VBase (0x0x141f78960) 64 virtual 
     vptridx=8u vbaseoffset=-24 vptr=((& Class::_ZTV5Class) + 88u) 

Nó không chia thành các biến thành viên (hoặc ít nhất là tôi không biết cách làm thế nào) nhưng bạn có thể nói phải nằm giữa độ lệch 28 và 64, giống như trong bố cục tiếng lóng.

Bạn có thể thấy rằng một lớp cơ sở được chọn là primary. Điều này loại bỏ nhu cầu điều chỉnh con trỏ this khi số Class được truy cập dưới dạng SBase1.

Tương đương cho gcc là:

$ g++ -fdump-class-hierarchy -c layout.cpp 

Tương đương cho Visual C++ như sau:

cl main.cpp /c /d1reportSingleClassLayoutTest_A 

see: https://blogs.msdn.microsoft.com/vcblog/2007/05/17/diagnosing-hidden-odr-violations-in-visual-c-and-fixing-lnk2022/

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