2013-05-29 18 views
10

Tôi muốn viết một đối tượng vào một tệp tuần tự bằng cách sử dụng fwrite. Lớp học giống như'Điều này' có được bảo đảm để trỏ đến sự khởi đầu của một đối tượng trong C++ không?

class A{ 
    int a; 
    int b; 
public: 
    //interface 
} 

và khi tôi viết một đối tượng vào một tệp. Tôi đang lang thang mà tôi có thể sử dụng fwrite(this, sizeof(int), 2, fo) để viết hai số nguyên đầu tiên.

Câu hỏi là: được this đảm bảo để trỏ đến đầu của đối tượng dữ liệu thậm chí nếu có thể có một bảng ảo tồn tại trong đầu của đối tượng. Vì vậy, các hoạt động trên là an toàn.

+0

Tại sao bạn không thử? Xuất bản a và b, thêm một phương thức ảo và sau đó kiểm tra địa chỉ của A :: a. – Spook

+0

@Spook Tôi đoán anh ấy đã thử trên một lớp POD, và nó đã làm việc :) –

+8

@Spook Tôi nghĩ anh ấy muốn biết tiêu chuẩn ISO nói gì. Ít nhất thì tôi cũng vậy. –

Trả lời

4

this cung cấp địa chỉ của đối tượng, không nhất thiết phải là địa chỉ của thành viên đầu tiên. Ngoại lệ duy nhất được gọi là bố cục tiêu chuẩn loại. Từ tiêu chuẩn C++ 11:

(9.2/20) Con trỏ đến đối tượng cấu trúc bố cục chuẩn, được chuyển đổi phù hợp sử dụng reinterpret_cast, trỏ đến thành viên ban đầu của nó (hoặc nếu thành viên đó là một trường bit , sau đó đến đơn vị nơi nó cư trú) và ngược lại. [Lưu ý: Do đó có thể có đệm không tên trong một đối tượng cấu trúc bố trí chuẩn, nhưng không phải ở đầu của nó, khi cần thiết để đạt được sự liên kết phù hợp.- cuối note]

Đây là định nghĩa của một loại tiêu chuẩn bố trí:

(9/7) Một lớp học tiêu chuẩn bố trí là một lớp rằng:
- không có người không tĩnh thành viên dữ liệu thuộc loại không bố cục tiêu chuẩn loại hoặc tham chiếu,
- không có chức năng ảo (10.3) và không có lớp cơ sở ảo (10.1),
- có cùng quyền kiểm soát truy cập (khoản 11) cho tất cả các thành viên dữ liệu không tĩnh,
- không có tiêu chuẩn-la các lớp cơ sở yout,
- hoặc không có thành viên dữ liệu không tĩnh trong lớp có nguồn gốc nhiều nhất và ít nhất một lớp cơ sở với thành viên dữ liệu không tĩnh hoặc không có lớp cơ sở với thành viên dữ liệu không tĩnh và
- không có các lớp cơ sở cùng loại với thành viên dữ liệu không tĩnh đầu tiên. [108]

[108] Điều này đảm bảo rằng hai subobjects có kiểu lớp giống nhau và thuộc về các đối tượng có nguồn gốc nhất cùng không được phân bổ tại cùng một địa chỉ (5.10).

Lưu ý rằng loại đối tượng không phải là POD – có bố cục chuẩn như được xác định ở trên là đủ. (POD tất cả đều có bố cục chuẩn, nhưng ngoài ra, chúng là trivially constructible, có thể di chuyển một cách trivially và có thể sao chép được.)

Theo như tôi có thể nói từ mã của bạn, kiểu của bạn có vẻ là bố cục chuẩn (đảm bảo kiểm soát truy cập là như nhau cho tất cả các thành viên dữ liệu không tĩnh). Trong trường hợp này, this thực sự sẽ trỏ đến thành viên ban đầu. Về việc sử dụng mục đích này cho mục đích tuần tự hóa, tiêu chuẩn thực sự nói rõ ràng:

(9/9) [Lưu ý: Các lớp bố cục chuẩn hữu ích khi giao tiếp với mã được viết bằng các ngôn ngữ lập trình khác. Bố trí của chúng được xác định trong 9.2. - lưu ý cuối cùng]

Tất nhiên điều này không giải quyết được các vấn đề về tuần tự hóa. Cụ thể, bạn sẽ không nhận được tính di động của dữ liệu được tuần tự hóa (ví dụ: do tính không tương thích của endianness).

+0

Tôi hiểu, vì vậy POD đề cập đến một cái gì đó giống như cấu trúc trong C, không cần hàm tạo, có ngữ nghĩa sao chép bitwise. – zoujyjs

+0

@zoujyjs Vâng, đó là một đặc điểm khá tốt (xem thêm [câu hỏi này] (http://stackoverflow.com/questions/146452/what-are-pod-types-in-c)). Nhưng một lần nữa, để có thể sử dụng 'this' để chỉ thành viên dữ liệu ban đầu, bạn không cần một POD. Bố cục tiêu chuẩn là đủ. – jogojapan

5

Không. Bạn có thể sử dụng fwrite(&a, sizeof(int), 2, fo), nhưng bạn cũng không nên. Chỉ cần dạo bộ qua bộ nhớ thô hiếm khi là một ý tưởng hay khi nói đến sự an toàn, bởi vì bạn không nên dựa vào các bố cục bộ nhớ cụ thể. Ai đó có thể giới thiệu một biến khác c giữa ab, mà không nhận thấy rằng anh ấy đang vi phạm mã của bạn. Nếu bạn muốn truy cập các biến của mình, hãy làm điều đó một cách rõ ràng. Không chỉ truy cập vào bộ nhớ mà bạn nghĩ rằng biến là hoặc nơi họ đã từng là lần cuối cùng bạn đã chọn.

+0

Vì vậy, cách đơn giản nhất và an toàn để thực hiện việc này là viết các thành viên một cách độc lập? Giống như 'fwrite (&a...); fwrite (& b)' v.v. Có đúng không? – zoujyjs

+0

nếu nó thực sự * có * là fwrite, vâng.Nhưng bạn thực sự nên xem xét việc sử dụng một số thư viện serialization. Các tệp bạn viết bằng fwrite không thể di chuyển được. –

+0

Nền tảng ABIs xác định bố cục cấu trúc cố định cho trao đổi dữ liệu. Có kiểu bố cục chuẩn và loại POD, là điều cần thiết để giao tiếp với hệ điều hành và thư viện được chia sẻ. Vì vậy, trong khi bạn không cần phải _stroll trên bộ nhớ raw_ bạn chắc chắn cần đảm bảo về bố trí, và nơi 'này' điểm. –

0

Viết một đối tượng vào một tệp bằng cách sử dụng fwrite là một ý tưởng rất tồi vì nhiều lý do. Ví dụ: nếu lớp học của bạn chứa số std::vector<int>, bạn sẽ lưu con trỏ vào số nguyên chứ không phải số nguyên.

Vì lý do "cấp cao hơn" (liên kết, phiên bản, tương thích nhị phân), cũng là một ý tưởng tồi trong hầu hết các trường hợp ngay cả trong C và thậm chí khi các thành viên chỉ là các kiểu gốc đơn giản.

1

Nhiều câu trả lời đã cho biết chính xác "Không". Dưới đây là một số mã đó chứng tỏ tại sao this không bao giờ được đảm bảo để trỏ đến đầu của đối tượng:

#include <iostream> 

class A { 
    public: virtual int value1() { std::cout << this << "\n"; } 
}; 

class B { 
    public: virtual int value2() { std::cout << this << "\n"; } 
}; 

class C : public A, public B {}; 

int main(int argc, char** argv) { 
    C* c = new C(); 
    A* a = (A*) c; 
    B* b = (B*) c; 
    a->value1(); 
    b->value2(); 
    return 0; 
} 

Xin lưu ý việc sử dụng this trong các phương pháp ảo.

Sản lượng có thể (tùy thuộc vào trình biên dịch) cho bạn biết rằng con trỏ ab là khác nhau. Nhiều khả năng, a sẽ trỏ đến điểm bắt đầu của đối tượng, nhưng b sẽ không. Vấn đề xuất hiện dễ dàng nhất khi nhiều thừa kế đang được sử dụng.

+0

@LuchianGrigore Đó là sự thật nếu bạn có thể đảm bảo rằng không có phôi trước đó đã xảy ra, tức là bạn có con trỏ ban đầu khi bắt đầu. Nếu bạn nhận được con trỏ thông qua một giao diện chức năng, không có sự bảo đảm như vậy. –

+0

@LuchianGrigore Ok, tôi đã thay đổi mã ví dụ để hiển thị rõ ràng việc sử dụng 'this'. Tôi không tranh cãi với các câu trả lời khác. Tôi cho thấy rằng liên kết giữa 'this' và _start của object_ là một khái niệm rất xa hoa. Ý định của tôi là thêm thông tin hữu ích. –

+0

Ah, tôi phải thừa nhận rằng tôi đã bỏ lỡ 'cout << this'. Lời xin lỗi của tôi. –

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