2013-02-26 9 views
9

Trong C++, có một vài lý do thuyết phục để sử dụng mảng C trên std::vector. Một trong số ít lý do thuyết phục, ít nhất là với C++ 03, là một thực tế là không thể sử dụng một vectơ để phân bổ một mảng các đối tượng chưa được khởi tạo. Các "lấp đầy" constructor cho std::vector là:Đối tượng được khởi tạo giá trị trong C++ 11 và std :: constructor vector

vector(size_type count, const T& value = T())

Nghĩa là ...

int* array = new array[1000000]; 

có khả năng là nhiều hiệu quả hơn:

std::vector<int> v(1000000); 

... từ constructor vector sẽ phải zero-initialize mảng các số nguyên. Do đó, khi làm việc với một vectơ POD, không có tương đương thực với malloc; tốt nhất bạn có thể nhận được là tương đương với calloc.

C++ 11 dường như đã thay đổi điều này, với khái niệm "khởi tạo giá trị". Trong C++ 11, std::vector có một hàm tạo mới nhận một giá trị size_type đơn lẻ, không có đối số mặc định. "Giá trị này khởi tạo" tất cả các phần tử trong vectơ. Tiêu chuẩn C++ 11 phân biệt giữa "khởi tạo giá trị" và "không khởi tạo".

Sự hiểu biết của tôi là "khởi tạo giá trị" tương đương với việc gọi hàm tạo mặc định trên T. Nếu T là loại POD như int, thì hàm tạo mặc định chỉ tạo một số nguyên chưa được khởi tạo. Do đó, trong C++ 11, explicit vector::vector(size_type count) thực sự tương đương với malloc nếu T là POD.

Tuy nhiên, sự hiểu biết của tôi về điều này dựa trên tiêu chuẩn dự thảo C++ 11, chứ không phải tiêu chuẩn cuối cùng.

Câu hỏi: Sự hiểu biết của tôi có chính xác ở đây không? Có phải explicit vector::vector(size_type count) cung cấp một mảng chưa được khởi tạo (tương tự như malloc) nếu T là POD không?

+5

Khởi tạo giá trị nghĩa là khởi tạo zero cho các kiểu tích hợp. – juanchopanza

+5

Nếu bạn muốn lưu trữ uninitialized sau đó sử dụng 'vector :: dự trữ', như mọi khi. – Pubby

+1

@Channel: C++ 03 cũng có giá trị khởi tạo như trái ngược với khởi tạo mặc định và khởi tạo không. Bit duy nhất có liên quan đã thay đổi là 'std :: vector'. –

Trả lời

21

Câu hỏi: Sự hiểu biết của tôi có chính xác ở đây không? Có explicit vector::vector(size_type count) cung cấp mảng không được khởi tạo (tương tự malloc) nếu T là POD không?

Không. Có sự khác biệt ở đây giữa C++ 03 và C++ 11, nhưng không phải vậy. Sự khác biệt là trong C++ 03, vector<T>(N) sẽ mặc định xây dựng một T và sau đó tạo N bản sao của nó để điền vectơ.

Trong khi ở C++ 11, vector<T>(N) sẽ điền vectơ theo mặc định xây dựng TN lần. Đối với các loại POD, hiệu ứng giống hệt nhau. Thật vậy, tôi hy vọng rằng đối với hầu như tất cả các loại hiệu quả là giống hệt nhau. Tuy nhiên đối với một cái gì đó giống như một unique_ptr (một loại di chuyển chỉ), sự khác biệt là rất quan trọng. Các ngữ nghĩa C++ 03 sẽ không bao giờ làm việc vì bạn không thể tạo một bản sao của kiểu di chuyển.

Vì vậy:

vector<unique_ptr<int>> v(10); 

tạo ra một vector của 10 unique_ptrs null (mà không phải là bản sao của nhau).

Trong trường hợp hiếm hoi mà nó làm cho một sự khác biệt và bạn cần phải hành vi C++ 03 có thể dễ dàng được thực hiện với:

vector<T> v(10, T()); 
+3

Sự khác biệt tiềm năng khác là nếu đó là một vectơ của một loại hoạt động như một tài nguyên được chia sẻ (sao cho tất cả các bản sao tham chiếu cùng một tài nguyên cơ bản). Nếu đó là cấu hình mặc định, C++ 03 sẽ tạo các bản sao N, tất cả tham chiếu cùng một tài nguyên cơ bản, trong khi C++ 11 sẽ tạo N đối tượng không liên quan. –

+0

@DaveS: Phải, cảm ơn. Tôi đã cập nhật câu trả lời với các hướng dẫn về cách khôi phục hành vi này. –

6

Lưu ý: các giá trị khởi tạo xảy ra trong cấp phát, vì vậy nếu bạn muốn một vector để khởi tạo mặc định thay vì khởi tạo giá trị cho các phần tử được tạo mặc định, bạn có thể làm điều gì đó như:

template<typename T> 
struct DefaultInitAllocator { 
    template<typename U> 
    void construct(U* p) 
    { ::new (static_cast<void*>(p)) U; } 

    template<typename U, typename... Args> 
    void construct(U* p, Args&&... args) 
    { ::new (static_cast<void*>(p)) U(std::forward<Args>(args)...); } 

    // ... rest of the allocator interface 
}; 

// ... 
typedef std::vector<int, DefaultInitAllocator<int>> DefaultInitVectorInt; 
+0

có vẻ như chỉ sử dụng 'reserve()' là ít phức tạp hơn. – jiggunjer

+0

Reserve() không cho phép bạn truy cập hợp pháp vào không gian. – Nevin

+0

khi bạn push_back sau khi đặt trước, không có phân bổ, phải không? – jiggunjer