2012-04-13 40 views
6

Tôi đã viết một hàm tải byte ra khỏi tệp và trả về cấu trúc FileData chứa bộ đệm byte và độ dài của bộ đệm.Sử dụng con trỏ thông minh trong cấu trúc hoặc lớp

Tôi muốn bộ đệm bị xóa ngay sau khi được tiêu thụ và bị loại bỏ khỏi phạm vi.

Tôi đang gặp sự cố khi biên dịch do các lỗi truyền khác nhau. Ngoài ra, tôi không chắc liệu bộ đệm đang được di chuyển đúng chứ không phải sao chép. Tôi không nhớ FileData struct được sao chép, vì nó có thể là 16 byte nhiều nhất.

Nói chung, làm cách nào để bạn sử dụng con trỏ thông minh làm trường lớp/cấu trúc? Đó có phải là thứ bạn muốn làm không?

Đây là một câu hỏi nhỏ, tôi biết, nhưng vì tôi đang gặp một số khó khăn về khái niệm với con trỏ thông minh nói chung, tôi hy vọng rằng ví dụ này sẽ giúp tôi đi đúng hướng.

Đây là những gì tôi đã có cho đến nay:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    unsigned int len; 
}; 

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    char* buf = new char[len]; 

    str.read(buf, len); 
    str.close(); 

    FileData d = { unique_ptr<char[]>(buf), len }; 

    return d; 
} 

Edit: Kể từ khi một số người đang tò mò về thông báo lỗi mà tôi nhận được với mã hiện này, ở đây nó là:

error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>' 
+0

Vấn đề của bạn là bạn không cung cấp bất kỳ chi tiết cụ thể nào về các thông báo lỗi. Làm thế nào trên trái đất chúng ta có thể có thể xác định chúng khác? – Puppy

+0

@DeadMG Tôi cho rằng rõ ràng là có vấn đề với mã, vì tôi đã chỉ ra rằng tôi không chắc đây là cách chính xác để sử dụng con trỏ thông minh và di chuyển ngữ nghĩa. Tôi muốn mã làm nhiều hơn biên dịch; Tôi muốn nó chính xác và thành ngữ. Tuy nhiên, tôi đã cập nhật câu hỏi với thông báo lỗi. –

+0

Lỗi bạn đang nhận được là vì bạn đang cố gắng sao chép một unique_ptr, bạn phải sử dụng std :: move. Bạn có thể đã sử dụng shared_ptr và khai báo deallocator của riêng bạn, nhưng giải pháp vector là sạch hơn nhiều. – pstrjds

Trả lời

5

Mã của bạn là tốt, ngoại trừ một chi tiết nhỏ:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    <del>unsigned int</del> <ins>streamoff</ins> len; 
}; 

Lý do nó không biên dịch cho bạn là trình biên dịch của bạn chưa triển khai thế hệ tự động của các thành viên di chuyển đặc biệt. Trong một trình biên dịch đầy đủ C++ 11 phù hợp FileData của bạn sẽ cư xử như thể:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    streamoff len; 

    FileData(FileData&&) = default; 
    FileData& operator=(FileData&&) = default; 
    FileData(const FileData&) = delete; 
    FileData& operator=(const FileData&) = delete; 
    ~FileData() = default; 
}; 

Các nhà xây dựng di chuyển defaulted chỉ cần di chuyển các cấu trúc mỗi thành viên (và tương tự cho việc chuyển nhượng di chuyển defaulted).

Khi trả lại d từ LoadFile, có một động thái tiềm ẩn diễn ra sẽ liên kết với hàm tạo di chuyển mặc định ngầm.

Sử dụng vector<char> hoặc string như những người khác đã đề xuất cũng sẽ hoạt động. Nhưng không có gì sai với mã của bạn như xa như C++ 11 là có liên quan.

Ồ, tôi có thể tinh chỉnh nó như vậy: Tôi thích để có được các nguồn lực của tôi sở hữu càng nhanh càng tốt:

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    FileData d = {unique_ptr<char[]>(new char[len]), len}; 

    str.read(d.buf.get(), d.len); 
    str.close(); 

    return d; 
} 

Nếu bạn cần phải xác định một cách rõ ràng các thành viên FileData di chuyển, nó sẽ giống như thế:

struct FileData 
{ 
    unique_ptr<char[]> buf; 
    streamoff len; 

    FileData(FileData&& f) 
     : buf(std::move(f.buf)), 
      len(f.len) 
     { 
      f.len = 0; 
     } 

    FileData& operator=(FileData&& f) 
    { 
     buf = std::move(f.buf); 
     len = f.len; 
     f.len = 0; 
     return *this; 
    } 
}; 

Ồ, điều này đưa tôi đến một điểm khác. Các thành viên di chuyển mặc định không phải là chính xác chính xác vì chúng không đặt len thành 0 trong nguồn. Nó phụ thuộc vào tài liệu của bạn nếu đây là một lỗi hay không. ~FileData() không yêu cầu len để phản ánh độ dài của bộ đệm. Nhưng các khách hàng khác có thể. Nếu bạn xác định được di chuyển từ FileData là không có số len đáng tin cậy, thì các thành viên di chuyển mặc định là tốt, nếu không thì sẽ không thành công.

+0

'FileData (FileData &&) = mặc định;' - đó là một cú pháp viết tắt hoạt động trên một số trình biên dịch? Khi tôi cố gắng tạo một hàm khởi tạo một cách rõ ràng, nó phàn nàn rằng 'toán tử =' không thể truy cập được. Hmm ... (VC++ 11, nhân tiện) –

+0

@ReiMiyasaka: Vâng, cú pháp '= default' là một cách để yêu cầu một cách rõ ràng những gì trong C++ 98/03 bạn sẽ gọi là thành viên đặc biệt được tạo ngầm . Làm việc cho ctor mặc định, sao chép ctor, gán bản sao, di chuyển ctor, di chuyển nhiệm vụ và dtor. Nhưng nó có thể chưa được thực hiện cho bạn (tôi không quen với VC++ 11). Các tính năng này đã được chuẩn hóa vào cuối trò chơi vì vậy có thể hiểu rằng trình biên dịch của bạn có thể chưa có chúng. Cú pháp "= delete" tương đương với khai báo nó riêng tư và không định nghĩa nó. Tôi không chắc chắn những gì đang xảy ra với ctor di chuyển rõ ràng của bạn. –

2

Tôi có thể sử dụng số std::vector thay vì std:::unique_ptr<char[]>, nếu bạn không nhớ số std::vector đang được sao chép khi bạn trả lại FileData:

struct FileData 
{ 
    vector<char> buf; 
}; 

FileData LoadFile(string filename) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    FileData d; 
    d.buf.resize(len); 

    str.read(&(d.buf)[0], len); 
    str.close(); 

    return d; 
} 

Ngoài ra, để tránh sao chép, người gọi có thể vượt qua trong một FileData như một tham số chức năng thay vì một giá trị trả về:

struct FileData 
{ 
    vector<char> buf; 
}; 

void LoadFile(string filename, FileData &data) 
{ 
    ifstream str; 
    str.open(filename, ios::binary); 

    str.seekg(0, ios::end); 
    auto len = str.tellg(); 
    str.seekg(0, ios::beg); 

    data.buf.resize(len); 

    str.read(&(data.buf)[0], len); 
    str.close(); 
} 
+0

Điều này không dẫn đến dữ liệu tập tin "mảng" chính nó được sao chép, hay không thực hiện vector ngữ nghĩa di chuyển C++ 11? Không cố gắng để được tranh luận, chỉ tò mò. – pstrjds

+0

Trong trường hợp đó, tôi thậm chí sẽ không cần cấu trúc FileData, do đó trả lời một nửa câu hỏi của tôi. Cảm ơn. Tôi lấy nó hiệu suất không phải là quá xấu với thay đổi kích thước một vector? –

+1

@Rei: Nó sẽ không thay đổi kích thước trong ví dụ đã cho. Vì ban đầu nó trống, nó giống như "size": P và nó không chậm hơn so với việc cấp phát bộ nhớ cho chính bạn. – Puppy

-1

Làm thế nào về việc sử dụng std :: string như bộ đệm. Nó có tất cả hành vi mà bạn muốn:

  • tài liệu tham khảo tính chứ không phải là sao chép
  • biến mất một lần ra khỏi phạm vi
  • giữ một lượng tùy ý các byte tùy ý

Mọi người sẽ xuống bỏ phiếu này bởi vì nó không phải là mục đích sử dụng ban đầu của chuỗi; có lẽ lấy được một lớp (hoặc quấn nó) và gọi nó là 'đệm'

+0

Tôi nghĩ điều đó sẽ hiệu quả, nhưng một lần nữa, nó không giúp tôi tìm ra cách tôi sử dụng con trỏ thông minh trong các tình huống tương tự khác. –

+0

Tôi không biết unique_ptr (tôi sử dụng tăng) nhưng unique_ptr có vẻ sai với tôi, thường là mảng ptr có lớp riêng của họ. Tôi sẽ có uniq_ptr . Về cơ bản nó sẽ hoạt động – pm100

+0

Khi nào 'std :: string' trở thành tham chiếu được tính?Hoặc là thực hiện cụ thể trong trình biên dịch khác nhau? –

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