Cách thích hợp để khởi tạo cấu trúc C ++


82

Mã của chúng tôi liên quan đến cấu trúc POD (Plain Old Datastruct) (đây là cấu trúc c ++ cơ bản có các cấu trúc và biến POD khác trong đó cần được khởi tạo ngay từ đầu).

Dựa trên những gì tôi đã đọc , có vẻ như:

myStruct = (MyStruct*)calloc(1, sizeof(MyStruct));

nên khởi tạo tất cả các giá trị bằng 0, như sau:

myStruct = new MyStruct();

Tuy nhiên, khi cấu trúc được khởi tạo theo cách thứ hai, Valgrind sau đó phàn nàn "việc nhảy hoặc di chuyển có điều kiện phụ thuộc vào (các) giá trị chưa được khởi tạo" khi các biến đó được sử dụng. Sự hiểu biết của tôi có sai sót ở đây, hay là Valgrind đang tung ra những kết quả tích cực sai?


1
Ngoài ra, lưu ý rằng new MyStruct()không bắt buộc phải đặt bất kỳ byte đệm nào trong C ++ 03. Trong C ++ 0x là như vậy. Bất kỳ bit đệm nào sẽ được đặt thành 0 trong C ++ 0x.
Johannes Schaub - litb

Cảm ơn bạn đã nắm bắt được, tôi đã chỉnh sửa câu hỏi của mình.
Shadow503

Câu hỏi thường gặp đã phát hành: stackoverflow.com/questions/620137/…
Robᵩ

Theo tôi hiểu mọi thứ, bạn sẽ không nhận được thông báo lỗi đó. Có lẽ cấu trúc dữ liệu của bạn không phải là POD? Bạn có thể giảm mã của mình xuống chương trình nhỏ nhất mà vẫn thể hiện hành vi này và đăng chương trình chắt lọc đó không? (Xem sscce.org ).
Robᵩ

Trình biên dịch nào? Bạn có thể đăng định nghĩa của MyStruct?
Alan Stokes

Câu trả lời:


120

Trong C ++, các lớp / cấu trúc giống hệt nhau (về cách khởi tạo).

Một cấu trúc không phải POD cũng có thể có một phương thức khởi tạo để nó có thể khởi tạo các thành viên.
Nếu cấu trúc của bạn là POD thì bạn có thể sử dụng trình khởi tạo.

struct C
{
    int x; 
    int y;
};

C  c = {0}; // Zero initialize POD

Ngoài ra, bạn có thể sử dụng hàm tạo mặc định.

C  c = C();      // Zero initialize using default constructor
C  c{};          // Latest versions accept this syntax.
C* c = new C();  // Zero initialize a dynamically allocated object.

// Note the difference between the above and the initialize version of the constructor.
// Note: All above comments apply to POD structures.
C  c;            // members are random
C* c = new C;    // members are random (more officially undefined).

Tôi tin rằng valgrind đang phàn nàn vì đó là cách C ++ được sử dụng để hoạt động. (Tôi không chắc chính xác khi nào C ++ được nâng cấp với cấu trúc mặc định khởi tạo bằng 0). Đặt cược tốt nhất của bạn là thêm một hàm tạo khởi tạo đối tượng (các cấu trúc là các hàm tạo được phép).

Một lưu ý phụ:
Rất nhiều người mới bắt đầu cố gắng đánh giá init:

C c(); // Unfortunately this is not a variable declaration.
C c{}; // This syntax was added to overcome this confusion.

// The correct way to do this is:
C c = C();

Tìm kiếm nhanh cho "Phân tích cú pháp gây khó chịu nhất" sẽ cung cấp lời giải thích tốt hơn tôi có thể.


1
Bạn có tài liệu tham khảo cho việc khởi tạo biến bằng 0 này trong C ++ không? Lần cuối tôi nghe nói, nó vẫn có hành vi tương tự như C. IIRC, trình biên dịch MSVC thậm chí đã loại bỏ các thành viên chưa khởi tạo không khởi tạo tại một số điểm (vì lý do hiệu suất, tôi giả sử), điều này rõ ràng đã tạo ra các vấn đề khá nghiêm trọng cho các bộ phận MS khác :)
Marc Mutz - mmutz

1
không, C()nhưng có vẻ như bạn đúng .
Marc Mutz - mmutz

4
Với một lớp POD, T, T()luôn có không khởi tạo các subobjects vô hướng. Tên của khởi tạo cấp cao nhất được gọi trong C ++ 98 khác với trong C ++ 03 ("khởi tạo mặc định" so với "khởi tạo giá trị"), nhưng hiệu ứng cuối đối với POD thì giống nhau. Cả C ++ 98 và C ++ 03 sẽ có tất cả các giá trị bằng không.
Johannes Schaub - litb

1
@RockyRaccoon C c();rất tiếc không phải là một khai báo biến mà là một khai báo chuyển tiếp của một hàm (Một hàm được gọi là 'c' không nhận tham số và trả về một đối tượng kiểu C). Google: Hầu hết các phân tích cú pháp Vexing
Martin York

1
@RockyRaccoon Sự khác biệt ở đây và với câu hỏi bạn liên kết đến trong nhận xét của mình; là trong trường hợp này không có tham số nào được sử dụng trong hàm tạo. Vì vậy, trình biên dịch gặp khó khăn khi phân tích cú pháp này và cho biết sự khác biệt giữa khai báo biến và khai báo chuyển tiếp của một hàm. Nó chọn khai báo hàm chuyển tiếp. Nếu bạn yêu cầu một tham số trong hàm khởi tạo thì trình biên dịch có thể xem đây là một khai báo biến mà không có bất kỳ vấn đề nào. C c(4);Là một khai báo biến hợp lệ.
Martin York

2

Theo những gì bạn đã nói với chúng tôi, nó có vẻ là một dương tính giả trong valgrind. Các newcú pháp với ()nên giá trị khởi tạo các đối tượng, giả sử nó là POD.

Có thể một số phần con trong cấu trúc của bạn không thực sự là POD và điều đó đang ngăn cản quá trình khởi tạo dự kiến? Bạn có thể đơn giản hóa mã của mình thành một ví dụ có thể đăng được mà vẫn gắn cờ lỗi giá trị không?

Mặt khác, có lẽ trình biên dịch của bạn không thực sự khởi tạo giá trị cấu trúc POD.

Trong mọi trường hợp, có lẽ giải pháp đơn giản nhất là viết (các) hàm tạo khi cần thiết cho các cấu trúc / phần con.


1

Tôi viết một số mã kiểm tra:

#include <string>
#include <iostream>
#include <stdio.h>

using namespace std;

struct sc {
    int x;
    string y;
    int* z;
};

int main(int argc, char** argv)
{
   int* r = new int[128];
   for(int i = 0; i < 128; i++ ) {
        r[i] = i+32;
   }
   cout << r[100] << endl;
   delete r;

   sc* a = new sc;
   sc* aa = new sc[2];
   sc* b = new sc();
   sc* ba = new sc[2]();

   cout << "az:" << a->z << endl;
   cout << "bz:" << b->z << endl;
   cout << "a:" << a->x << " y" << a->y << "end" << endl;
   cout << "b:" << b->x << " y" << b->y <<  "end" <<endl;
   cout << "aa:" << aa->x << " y" << aa->y <<  "end" <<endl;
   cout << "ba:" << ba->x << " y" << ba->y <<  "end" <<endl;
}

g ++ biên dịch và chạy:

./a.out 
132
az:0x2b0000002a
bz:0
a:854191480 yend
b:0 yend
aa:854190968 yend
ba:0 yend

Ok, tôi không quá quen thuộc với new sc;vs new sc();. Tôi đã nghĩ rằng các parens bị thiếu là một lỗi. Nhưng điều này dường như để chứng minh rằng câu trả lời là đúng (nó init 0 khi bạn sử dụng constructor mặc định.)
nycynik

0

Bạn cần khởi tạo bất kỳ thành viên nào bạn có trong cấu trúc của mình, ví dụ:

struct MyStruct {
  private:
    int someInt_;
    float someFloat_;

  public:
    MyStruct(): someInt_(0), someFloat_(1.0) {} // Initializer list will set appropriate values

};

Trong trường hợp này, tôi không sử dụng một lớp mà là một cấu trúc.
Shadow503,

Một cấu trúc là một lớp. Đã chỉnh sửa bài đăng của tôi :)
ralphtheninja

2
Bạn không cần phải làm điều này đối với POD
Alan Stokes,

0

Vì đó là một cấu trúc POD, bạn luôn có thể ghi nhớ nó thành 0 - đây có thể là cách dễ nhất để khởi tạo các trường (giả sử điều đó là phù hợp).


1
Cho dù đó là một cấu trúc hay một lớp không liên quan gì đến khả năng ghi nhớ hoặc tạo một phương thức khởi tạo. Xem ví dụ: stackoverflow.com/questions/2750270/cc-struct-vs-class
Erik

Thông thường bạn sẽ không ghi nhớ một lớp sau khi tạo nó, vì phương thức khởi tạo của nó (có lẽ) đã thực hiện việc khởi tạo.
Scott C Wilson

1
không có sự khác biệt giữa "class" và "struct" trong C ++, ngoại trừ bảo vệ mặc định (private và public, tương ứng). Một cấu trúc có thể là một phi POD và một lớp có thể là một POD, những khái niệm này là trực giao.
Marc Mutz - mmutz

@mmutz và @Erik - đã đồng ý - đã rút ngắn câu trả lời của tôi để tránh nhầm lẫn.
Scott C Wilson

memset () không phải là lựa chọn tốt nhất, bạn không thể khởi tạo cấu trúc POD bằng cách gọi ctor mặc định của nó.
Nils

0

Đó dường như là cách dễ nhất đối với tôi. Các thành viên cấu trúc có thể được khởi tạo bằng dấu ngoặc nhọn '{}'. Ví dụ, sau đây là một khởi tạo hợp lệ.

struct Point 
{ 
   int x, y; 
};  

int main() 
{ 
   // A valid initialization. member x gets value 0 and y 
   // gets value 1.  The order of declaration is followed. 
   struct Point p1 = {0, 1};  
}

Có thông tin tốt về cấu trúc trong c ++ - https://www.geeksforgeeks.org/structures-in-cpp/

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.