2012-04-22 25 views
6

Lớp học chứa std::vector<int*>. Mã bên ngoài cần quyền truy cập chỉ đọc vào vectơ này, không được sửa đổi nội dung (không phải con trỏ hoặc nội dung của chúng). Bên trong lớp, các giá trị thể thay đổi (ví dụ double_values(), và do đó lưu trữ chúng như là một std::vector<const int*> là không thể.Xử lý véc tơ <int*> làm vector <const int*> mà không cần sao chép (C++ 0x)

Có cách nào để trả lại std::vector<int*> như một std::vector<const int*> mà không làm một bản sao? Nó cảm thấy như có nên được, vì const chỉ đơn giản là hoạt động ở thời gian biên dịch để nói những gì có thể và không thể được sửa đổi

Mã số:. (biên dịch với g++ -std=c++0x)

class ReadOnlyAccess 
{ 
public: 
    ReadOnlyAccess(const std::vector<int*> & int_ptrs_param): 
    int_ptrs(int_ptrs_param) 
    { 
    } 
    const std::vector<int*> & get_int_ptrs() const 
    { 
    return int_ptrs; 
    } 
    std::vector<const int*> safely_get_int_ptrs() const 
    { 
    // will not compile (too bad): 
    // return int_ptrs; 

    // need to copy entire vector 
    std::vector<const int*> result(int_ptrs.size()); 
    for (int k=0; k<int_ptrs.size(); k++) 
     result[k] = int_ptrs[k]; 
    return result; 
    } 
    void double_values() 
    { 
    for (int*p : int_ptrs) 
     *p *= 2; 
    } 
    void print() const 
    { 
    for (const int * p : int_ptrs) 
     std::cout << *p << " "; 
    std::cout << std::endl; 
    } 
private: 
    std::vector<int*> int_ptrs; 
}; 

int main() { 
    ReadOnlyAccess roa(std::vector<int*>{new int(10), new int(20), new int(100)}); 
    std::vector<const int*> safe_int_ptrs = roa.safely_get_int_ptrs(); 
    // does not compile (good) 
    // *safe_int_ptrs[0] = -100000; 
    roa.print(); 

    const std::vector<int*> & int_ptrs = roa.get_int_ptrs(); 
    // changes are made to the internal class values via the accessor! nooooo! 
    *int_ptrs[0] = -100000; 
    roa.print(); 

    return 0; 
} 
+0

bản sao có thể có của [vector và const] (http://stackoverflow.com/questions/2102244/vector-and-const) –

+0

xem http://stackoverflow.com/questions/2868485/cast-vectort-to- vectorconst-t – WeaselFox

+2

@Bo: Câu hỏi đó không trả lời câu hỏi này, thực sự. Và WeaselFox 'cũng không liên quan nữa vì con trỏ vô hướng. – Xeo

Trả lời

4

Retu rning các vector sẽ ngụ ý một bản sao nếu bạn muốn giữ cho con trỏ const anyway. Tuy nhiên, nếu mục tiêu của bạn là cung cấp cách sử dụng các giá trị mà không sửa đổi hoặc sửa đổi vùng chứa, thì thuật toán dựa trên mô hình khách truy cập có thể là giải pháp rất tốt, đặc biệt là bây giờ chúng ta có thể sử dụng các biểu thức lambda:

#include <vector> 
#include <iostream> 

class Data 
{ 
public: 

    //...whatever needed to fill the values 

    // here we assume that Func is equivalent to std::function< void (int)> or std::function< void (const int&) > and can return anything that will be ignored here. 
    template< class Func > 
    void for_each_value(Func func) const // read-only 
    { 
     for(const int* value : m_values) // implicit conversion 
     { 
      func(*value); // read-only reference (const &), or copy 
      // if func needs to work with the adress of the object, it still can by getting a reference to it and using & to get it's adress 
     } 
    } 


    void print() const 
    { 
     std::cout << "\nData values: \n"; 
     for_each_value([](const int value) { std::cout << " "<< value << '\n'; }); 
    } 

    void count_values() const { return m_values.size(); } 

private: 

    std::vector<int*> m_values; 

}; 



int main() 
{ 
    Data data; 
    // ... whatever needed to fill the data 

    data.print();  

    std::vector<int> modified_values; 
    data.for_each_value([&](int value) { modified_values.push_back(value + 42); }); 

    return 0; 
} 

Nếu bạn hiểu rằng, và những cách khác nhau để sử dụng các giá trị có thể được giảm đến một vài thuật toán nửa chung, sau đó nó sẽ làm cho mã của bạn đơn giản và cho phép bạn để giữ cho dữ liệu bên trong cấu trúc của bạn thay vì phơi bày đó là ruột.

+0

Bạn biết về 'std :: for_each', phải không? – jalf

+0

Oviously. Tại sao bạn hỏi, chính xác? Ở đây std :: for_each được sử dụng bên ngoài của lớp sẽ không giúp đỡ vì nó sẽ yêu cầu để lộ ra các iterator container thực sự, hoặc tạo một bản sao, đó là để tránh được bằng câu hỏi. Tôi chỉ có thể thay thế vòng lặp for_each_value bằng lệnh std :: for_each, không có lợi ích. Điểm của câu trả lời của tôi là không có bản sao của vùng chứa và bất kỳ thuật toán nào cho truyền tải, bạn không cần phải hiển thị nội dung của lớp IF nếu bạn biết loại người dùng nào cần đến trong lớp. Rõ ràng, nếu bạn không biết, hãy ôm lấy YAGNI. – Klaim

+0

Điều đó đang được nói, một số trường hợp yêu cầu lớp đưa ra nội dung bằng trình vòng lặp. Nhưng tôi không nghĩ đó là điều cần thiết cho câu hỏi cụ thể. – Klaim

1

Bạn có thể cung cấp chế độ xem cho các giá trị const thông qua trình vòng lặp tùy chỉnh. Một cách dễ dàng sẽ được sử dụng boost::iterator:

#include <boost/iterator/indirect_iterator.hpp> 

class ReadOnlyAccess 
{ 
// ... 
    typedef boost::indirect_iterator<const int* const*, const int> const_val_iter_type; 
    const_val_iter_type cval_begin() { 
     return it_t{const_cast<const int* const*>(&int_ptrs[0])}; 
    } 
} 

int main() { 
    // ... 
    auto x = roa.cval_begin(); 
    std::cout << x[0] <<' ' << x[1] << x[2] <<'\n'; 
    // we can still access the pointers themselves via .base() member function: 
    for (int i=0; i<3; ++i) 
     assert(x.base()[i] == safe_int_ptrs[i]); 
    // the values are read-only, the following does not compile: 
    // x[0] = -1; 
    // **x.base() = -1; 
    // *x.base() = nullptr; 
} 

Nếu chúng ta sử dụng boost::indirect_iterator<typename std::vector<int*>::const_iterator, const int> cho const_val_iter_type, chúng tôi có thể sửa đổi các giá trị nhọn qua .base() (nhưng không trực tiếp như trong ví dụ x[0] = -1), vì vậy giải pháp này không phải là nói chung.

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