2010-02-05 62 views
126

Tôi chỉ mới bắt đầu tham gia C++ và tôi muốn thu thập một số thói quen tốt. Nếu tôi vừa phân bổ một mảng kiểu int với toán tử new, làm thế nào tôi có thể khởi tạo tất cả thành 0 mà không lặp lại tất cả chúng? Tôi có nên sử dụng memset không? Có “ C++ ” cách để làm điều đó không?Làm thế nào để khởi tạo bộ nhớ với toán tử mới trong C++?

+15

Nếu bạn muốn lấy một thói quen C++ tốt, hãy tránh sử dụng mảng trực tiếp và sử dụng véc tơ thay thế. Vector sẽ khởi tạo tất cả các mục bất kể loại và sau đó bạn không cần phải nhớ gọi toán tử delete []. – brianegge

+0

@brianegge: Điều gì sẽ xảy ra nếu tôi cần truyền một mảng tới một hàm C bên ngoài, tôi có thể chỉ cho nó véc tơ không? – dreamlax

+11

Bạn có thể chuyển '& vectơ [0]'. – jamesdlin

Trả lời

302

Đó là một tính năng đáng ngạc nhiên ít được biết đến của C++ (được minh chứng bởi thực tế là không ai đã cho điều này như một câu trả lời nào), nhưng nó thực sự có cú pháp đặc biệt cho default- khởi tạo một mảng (tốt, về mặt kỹ thuật, nó được gọi là "giá trị khởi tạo" trong tiêu chuẩn):

new int[10](); 

Lưu ý rằng bạn phải sử dụng dấu ngoặc rỗng - bạn có thể không, ví dụ, sử dụng (0) hoặc bất kỳ biểu hiện khác (đó là lý do tại sao điều này chỉ hữu ích cho initia mặc định lization).

này được cho phép rõ ràng ISO C++ 03 5.3.4 [expr.new]/15, trong đó nói rằng:

Một mới thể hiện tạo ra một đối tượng kiểu T khởi đối tượng đó như sau :

...

  • Nếu trình khởi tạo mới có dạng(), mục được khởi tạo giá trị (8.5);

và không hạn chế các loại mà điều này được cho phép, trong khi các hình thức (expression-list) bị hạn chế một cách rõ ràng bởi các quy tắc thêm trong phần tương tự như vậy mà nó không cho phép loại mảng.

+1

Trong khi tôi đồng ý rằng điều này ít được biết đến, tôi không thể (hoàn toàn) đồng ý rằng nó thực sự rất đáng ngạc nhiên - nó được thêm vào trong C++ 03, mà hầu hết mọi người dường như gần như bị bỏ qua (vì đây là một trong số ít những điều nó đã thêm). –

+1

@Jerry: Tôi phải thừa nhận rằng tôi chưa biết (có lẽ vì khi tôi đọc chuẩn, nó đã là C++ 03 rồi). Điều đó nói rằng, nó là đáng chú ý rằng tất cả các triển khai tôi biết hỗ trợ này (tôi đoán đó là bởi vì nó là như vậy tầm thường để thực hiện). –

+1

Vâng, nó là khá tầm thường để thực hiện. Theo như mới, * tất cả * "khởi tạo giá trị" là mới trong C++ 03. –

6

Có có:

std::vector<int> vec(SIZE, 0); 

Sử dụng một vector thay vì một mảng cấp phát động. Lợi ích bao gồm không phải bận tâm với việc xóa một cách rõ ràng mảng (nó sẽ bị xóa khi vectơ đi ra khỏi phạm vi) và cũng là bộ nhớ sẽ tự động bị xóa ngay cả khi có một ngoại lệ được ném.

Chỉnh sửa: Để tránh giảm bớt sự cố từ những người không bận tâm đọc nhận xét bên dưới, tôi nên làm rõ hơn rằng câu trả lời này không nói rằng véc tơ là luôn là câu trả lời đúng của. Nhưng nó chắc chắn là một cách C++ hơn "thủ công" đảm bảo xóa một mảng.

Bây giờ với C++ 11, cũng có std :: mảng mô hình mảng có kích thước không đổi (so với vectơ có thể phát triển). Ngoài ra còn có std :: unique_ptr quản lý mảng được phân bổ động (có thể được kết hợp với khởi tạo như được trả lời trong các câu trả lời khác cho câu hỏi này). Bất kỳ cách nào trong số đó là cách C++ hơn là xử lý thủ công con trỏ tới mảng, IMHO.

+10

điều này không thực sự trả lời câu hỏi đã được hỏi. –

+1

Tôi có nên luôn sử dụng 'std :: vector' thay vì mảng được phân bổ động không? Những lợi ích của việc sử dụng một mảng trên một vectơ là gì và ngược lại? – dreamlax

+0

Tôi nên đề cập đến trong câu hỏi của mình, rằng mảng phải tồn tại sau phạm vi hiện tại. Tôi có phải sử dụng 'new std :: vector'? – dreamlax

23

Giả sử rằng bạn thực sự làm muốn một mảng và không phải là một std :: vector, "C++ cách" sẽ là này

#include <algorithm> 

int* array = new int[n]; // Assuming "n" is a pre-existing variable 

std::fill_n(array, n, 0); 

Nhưng lưu ý rằng dưới mui xe này vẫn là thực sự chỉ là một vòng lặp mà gán mỗi phần tử cho 0 (thực sự không có cách nào khác để làm điều đó, chặn một kiến ​​trúc đặc biệt với hỗ trợ mức phần cứng).

+0

Tôi không quan tâm nếu vòng lặp được thực hiện bên dưới một hàm, tôi chỉ muốn biết có hay không tôi đã phải thực hiện một vòng lặp như vậy bản thân mình. Cảm ơn vì tiền hỗ trợ. – dreamlax

+3

Bạn có thể ngạc nhiên. Tôi đã. Trên STL của tôi (cả GCC và Dinkumware), std :: bản sao thực sự biến thành một memcpy nếu nó phát hiện nó đang được gọi với xây dựng trong các loại. Tôi sẽ không ngạc nhiên nếu std :: fill_n sử dụng memset. –

+2

Không. Sử dụng 'Value-Initialization' để đặt tất cả thành viên thành 0. –

2

std::fill là một cách. Đưa hai vòng lặp và một giá trị để điền vào vùng bằng. Điều đó, hoặc vòng lặp for, sẽ (tôi giả sử) là cách C++ hơn.

Để đặt một mảng các loại số nguyên gốc thành 0 cụ thể, memset là tốt, mặc dù nó có thể làm tăng lông mày. Hãy xem xét cũng calloc, mặc dù nó là một chút bất tiện để sử dụng từ C + + vì các diễn viên.

Về phần mình, tôi luôn sử dụng vòng lặp.

(Tôi không thích thứ hai đoán ý định của người dân, nhưng nó là sự thật rằng std::vector là, tất cả mọi thứ là như nhau, thích hợp hơn để sử dụng new[].)

0

Điển hình cho các danh sách động các hạng mục, bạn sử dụng một std::vector.

Nói chung tôi sử dụng memset hoặc vòng lặp cho phân bổ động bộ nhớ thô, tùy thuộc vào biến tôi dự đoán khu vực mã đó trong tương lai.

7

Nếu bộ nhớ bạn đang cấp phát là một lớp có hàm tạo thực hiện điều gì đó hữu ích, toán tử mới sẽ gọi hàm khởi tạo đó và để đối tượng của bạn được khởi tạo. Nếu bạn đang phân bổ POD hoặc thứ gì đó không có hàm khởi tạo để khởi tạo trạng thái của đối tượng, thì bạn không thể cấp phát bộ nhớ và khởi tạo bộ nhớ đó với toán tử mới trong một thao tác.Tuy nhiên, bạn có một số tùy chọn:

1) Sử dụng biến ngăn xếp thay thế. Bạn có thể phân bổ và default-initialize trong một bước, như thế này:

int vals[100] = {0}; // first element is a matter of style 

2) sử dụng memset(). Lưu ý rằng nếu đối tượng bạn đang phân bổ không phải là POD, việc ghi nhớ đó là một ý tưởng tồi. Một ví dụ cụ thể là nếu bạn nhớ một lớp có chức năng ảo, bạn sẽ thổi đi vtable và để đối tượng của bạn ở trạng thái không sử dụng được.

3) Nhiều hệ điều hành có các cuộc gọi thực hiện những gì bạn muốn - phân bổ trên một đống và khởi tạo dữ liệu cho một thứ gì đó. Ví dụ về Windows sẽ là VirtualAlloc()

4) Đây thường là tùy chọn tốt nhất. Tránh phải tự quản lý bộ nhớ. Bạn có thể sử dụng STL container để làm chỉ là về bất cứ điều gì bạn sẽ làm gì với nguyên bộ nhớ, bao gồm phân bổ và khởi tạo tất cả trong một ngã swoop:

std::vector<int> myInts(100, 0); // creates a vector of 100 ints, all set to zero 
1

bạn luôn có thể sử dụng memset:

int myArray[10]; 
memset(myArray, 0, 10 * sizeof(int)); 
+0

Tôi hiểu rằng tôi có thể sử dụng 'memset', nhưng tôi không chắc đây có phải là cách C++ để tiếp cận vấn đề hay không. – dreamlax

+0

Yup, memset hoạt động tốt như trong C++ như trong C để thiết lập bộ nhớ tuyến tính thành một giá trị. –

+1

Nó không thực sự là 'cách C++', nhưng sau đó không phải là mảng thô. –

17

Có số của các phương pháp để phân bổ một mảng của loại nội tại và tất cả các phương pháp này là chính xác, mặc dù một trong những phương pháp để lựa chọn, phụ thuộc ...

Khởi tạo thủ công tất cả các phần tử trong vòng

int* p = new int[10]; 
for (int i = 0; i < 10; i++) 
{ 
    p[i] = 0; 
} 

Sử dụng std::memset chức năng từ <cstring>

int* p = new int[10]; 
std::memset(p, 0, 10); 

Sử dụng thuật toán std::fill_n từ <algorithm>

int* p = new int[10]; 
std::fill_n(p, 10, 0); 

Sử dụng std::vector chứa

std::vector<int> v(10); // elements zero'ed 

Nếu C++0x có sẵn, sử dụng initializer list tính năng

int a[] = { 1, 2, 3 }; // 3-element static size array 
vector<int> v = { 1, 2, 3 }; // 3-element array but vector is resizeable in runtime 
+0

phải là vector Nếu bạn đã thêm p = new int [10]() bạn đã có danh sách đầy đủ. – karsten

+0

@karsten Cố định, chúc mừng. – mloskot

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