Làm cách nào để khởi tạo std :: vector từ mảng kiểu C?


174

Cách rẻ nhất để khởi tạo một std::vectormảng từ kiểu C là gì?

Ví dụ: Trong lớp sau, tôi có một vector, nhưng do các hạn chế bên ngoài, dữ liệu sẽ được truyền dưới dạng mảng kiểu C:

class Foo {
  std::vector<double> w_;
public:
  void set_data(double* w, int len){
   // how to cheaply initialize the std::vector?
}

Rõ ràng, tôi có thể gọi w_.resize()và sau đó lặp qua các phần tử, hoặc gọi std::copy(). Có phương pháp nào tốt hơn không?


11
Mấu chốt của vấn đề là không có cách nào để vectơ biết nếu cùng một cấp phát được sử dụng để tạo mảng kiểu C của bạn. Vì vậy, vectơ phải phân bổ bộ nhớ bằng cách sử dụng cấp phát riêng của nó. Nếu không, nó chỉ có thể trao đổi mảng bên dưới và thay thế nó bằng mảng của bạn.
Vô hiệu

Câu trả lời:


233

Đừng quên rằng bạn có thể coi con trỏ như các trình vòng lặp:

w_.assign(w, w + len);

3
Đó là một vấn đề chất lượng thực hiện. Vì các trình vòng lặp có các thẻ chỉ định danh mục của chúng, nên việc triển khai assignchắc chắn là miễn phí để sử dụng chúng để tối ưu hóa; ít nhất là trong VC ++, nó thực sự làm điều đó.
Pavel Minaev

38
Giải pháp nhanh có thể là std :: vector <double> w_ (w, w + len);
kẹt

1
Điều này sao chép các phần tử vào một bộ lưu trữ mới được tạo cho 'w_'; 'w_.data' sẽ không trỏ đến 'w'. Bạn vẫn phải giải quyết 'w'. Không có quyền chuyển nhượng quyền sở hữu
Indy9000

1
Nếu đó là một phần tử vượt quá kết thúc, nó sẽ ổn (giống như v.end()một trình vòng lặp chỉ một điểm qua phần cuối với vectơ trong trường hợp tương tự). Nếu bạn nhận được một khẳng định, sau đó một cái gì đó tắt ở nơi khác.
Pavel Minaev

1
Chỉ cần nhanh chóng, điều này sẽ giải quyết bộ nhớ mảng khi vector đi ra khỏi phạm vi?
Adrian Koch

41

Bạn sử dụng từ khởi tạo để không rõ đây là bài tập một lần hay có thể xảy ra nhiều lần.

Nếu bạn chỉ cần khởi tạo một lần, bạn có thể đặt nó vào hàm tạo và sử dụng hai hàm tạo vector lặp:

Foo::Foo(double* w, int len) : w_(w, w + len) { }

Nếu không, sử dụng gán như đề xuất trước đây:

void set_data(double* w, int len)
{
    w_.assign(w, w + len);
}

1
Trong trường hợp của tôi, sự phân công sẽ xảy ra nhiều lần.
Frank

12

Bạn có thể 'tìm hiểu' kích thước của mảng tự động:

template<typename T, size_t N>
void set_data(const T (&w)[N]){
    w_.assign(w, w+N);
}

Hy vọng, bạn có thể thay đổi giao diện thành set_data như trên. Nó vẫn chấp nhận một mảng kiểu C là đối số đầu tiên của nó. Nó chỉ xảy ra để đưa nó bằng cách tham khảo.


Làm thế nào nó hoạt động

[Cập nhật: Xem tại đây để thảo luận toàn diện hơn về việc tìm hiểu kích thước]

Đây là một giải pháp tổng quát hơn:

template<typename T, size_t N>
void copy_from_array(vector<T> &target_vector, const T (&source_array)[N]) {
    target_vector.assign(source_array, source_array+N);
}

Điều này hoạt động vì mảng đang được truyền dưới dạng tham chiếu đến mảng. Trong C / C ++, bạn không thể truyền một mảng dưới dạng hàm, thay vào đó nó sẽ phân rã thành một con trỏ và bạn mất kích thước. Nhưng trong C ++, bạn có thể chuyển một tham chiếu đến mảng.

Vượt qua một mảng bằng tham chiếu yêu cầu các loại khớp chính xác. Kích thước của một mảng là một phần của loại của nó. Điều này có nghĩa là chúng ta có thể sử dụng tham số mẫu N để tìm hiểu kích thước cho chúng ta.

Có thể còn đơn giản hơn khi có hàm này trả về một vectơ. Với tối ưu hóa trình biên dịch phù hợp có hiệu lực, điều này sẽ nhanh hơn vẻ ngoài của nó.

template<typename T, size_t N>
vector<T> convert_array_to_vector(const T (&source_array)[N]) {
    return vector<T>(source_array, source_array+N);
}

1
Trong mẫu cuối cùng, return { begin(source_array), end(source_array) };cũng có thể
MM

12

Chà, Pavel đã rất gần, nhưng thậm chí còn có một giải pháp đơn giản và thanh lịch hơn để khởi tạo một container tuần tự từ mảng kiểu ac.

Trong trường hợp của bạn:

w_ (array, std::end(array))
  • mảng sẽ cho chúng ta một con trỏ đến đầu mảng (không bắt được tên của nó),
  • std :: end (mảng) sẽ đưa chúng ta một iterator đến cuối mảng.

1
Những gì bao gồm / phiên bản của C ++ không yêu cầu này?
Vlad

1
Đây là một trong những hàm tạo của std :: vector từ ít nhất là c ++ 98 trở đi .... Nó được gọi là 'hàm tạo phạm vi'. cplusplus.com/reference/vector/vector/vector Hãy thử nó.
Mugurel

1
Phiên bản độc lập hơn là: w_ (std :: started (mảng), std :: end (mảng)); (Trong tương lai bạn có thể thay đổi mảng C cho vùng chứa C ++).
Andrew Romanov

13
Xin lưu ý bạn, điều này chỉ hoạt động nếu bạn có một bản sao thực array(thường có nghĩa là bạn đang sao chép từ một mảng toàn cầu hoặc cục bộ (được khai báo trong hàm hiện tại). Trong trường hợp của OP, anh ta nhận được một con trỏ và độ dài, và vì nó không được đặt theo chiều dài, nên họ không thể thay đổi để nhận một con trỏ tới một mảng có kích thước hoặc bất cứ thứ gì, vì vậy std::endsẽ không hoạt động.
ShadowRanger

2
vectorkhông quá tải operator(), vì vậy điều này sẽ không được biên dịch. std::endđược gọi trên một con trỏ cũng không được sử dụng (câu hỏi yêu cầu gán một vectơ từ một con trỏ và một biến có độ dài riêng). Nó sẽ cải thiện câu trả lời của bạn để hiển thị nhiều ngữ cảnh hơn về những gì bạn đang cố gắng đề xuất
MM

2

Câu trả lời chung chung nhanh chóng:

std::vector<double> vec(carray,carray+carray_size); 

hoặc câu hỏi cụ thể:

std::vector<double> w_(w,w+len); 

dựa trên : Đừng quên rằng bạn có thể coi con trỏ như các trình vòng lặp


0

std::vector<double>::assignlà cách để đi, bởi vì đó là ít mã . Nhưng làm thế nào nó hoạt động, thực sự? Nó không thay đổi kích thước và sau đó sao chép? Trong MS thực hiện STL tôi đang sử dụng nó chính xác như vậy.

Tôi e rằng không có cách nào nhanh hơn để thực hiện (tái) việc khởi tạo của bạn std::vector.


Điều gì xảy ra nếu dữ liệu được chia sẻ giữa vectơ và một mảng? Chúng ta có cần sao chép bất cứ điều gì trong trường hợp này không?
Vlad

Đó là một câu trả lời hay một câu hỏi? nó mang lại những gì cho câu trả lời đã có sẵn?
Jean-François Fabre

@ Jean-FrançoisFabre và nhận xét của bạn mang lại điều gì? ;) đúng, đó là một câu trả lời kém được đưa ra từ lâu.
Janusz Lenar
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.