Các cụm từ sau có nghĩa gì trong C ++: zero-, default- và khởi tạo giá trị?


188

Các cụm từ sau có nghĩa gì trong C ++:

  • không khởi tạo,

  • khởi tạo mặc định và

  • khởi tạo giá trị

Nhà phát triển C ++ nên biết gì về họ?


1
Điều này có liên quan đến (nhưng không giống với) stackoverflow.com/questions/620137/ Kẻ
Steve Jessop

20
Còn nữa! Danh sách đầy đủ các khởi tạo: Giá trị, trực tiếp, sao chép, danh sách (giới thiệu mới C ++ 11), tổng hợp, tham chiếu, không, không đổi và mặc định; en.cppreference.com/w/cpp/lingu/initialization liệt kê tất cả chúng với các ví dụ :)
legends2k

Câu trả lời:


64

Một điều cần nhận ra là 'khởi tạo giá trị' là mới với tiêu chuẩn C ++ 2003 - nó không tồn tại trong tiêu chuẩn gốc năm 1998 (tôi nghĩ đó có thể là sự khác biệt duy nhất không chỉ là làm rõ). Xem câu trả lời của Kirill V. Lyadvinsky cho các định nghĩa trực tiếp từ tiêu chuẩn.

Xem câu trả lời trước này về hành vi operator newđể biết chi tiết về hành vi khác nhau của các loại khởi tạo này và khi chúng khởi động (và khi chúng khác nhau từ c ++ 98 đến C ++ 03):

Điểm chính của câu trả lời là:

Đôi khi, bộ nhớ được trả về bởi toán tử mới sẽ được khởi tạo và đôi khi, bộ nhớ sẽ không phụ thuộc vào loại bạn mới cập nhật là POD hay nếu đó là lớp chứa các thành viên POD và đang sử dụng hàm tạo mặc định do trình biên dịch tạo .

  • Trong C ++ 1998, có 2 loại khởi tạo: không và mặc định
  • Trong C ++ 2003, loại khởi tạo thứ 3, khởi tạo giá trị đã được thêm vào.

Để nói rằng ít nhất, nó khá phức tạp và khi các phương pháp khác nhau phát huy tác dụng.

Một điều chắc chắn cần lưu ý là MSVC tuân theo các quy tắc C ++ 98, ngay cả trong VS 2008 (VC 9 hoặc cl.exe phiên bản 15.x).

Đoạn mã sau đây cho thấy MSVC và Digital Mars tuân theo quy tắc C ++ 98, trong khi GCC 3.4.5 và Comeau tuân theo quy tắc C ++ 03:

#include <cstdio>
#include <cstring>
#include <new>

struct A { int m; }; // POD
struct B { ~B(); int m; }; // non-POD, compiler generated default ctor
struct C { C() : m() {}; ~C(); int m; }; // non-POD, default-initialising m

int main()
{
    char buf[sizeof(B)];
    std::memset( buf, 0x5a, sizeof( buf));

    // use placement new on the memset'ed buffer to make sure 
    //  if we see a zero result it's due to an explicit 
    //  value initialization
    B* pB = new(buf) B();   //C++98 rules - pB->m is uninitialized
                            //C++03 rules - pB->m is set to 0
    std::printf( "m  is %d\n", pB->m);
    return 0;
}

1
Không phải là nó quan trọng int, nhưng m()trên giá trị dòng thứ ba khởi tạo m. Quan trọng nếu bạn thay đổi int m;thành B m;. :)
Julian Schaub - litb

Phải - ACkhông được sử dụng trong ví dụ này (chúng được chuyển từ câu trả lời được liên kết khác). Mặc dù C ++ 98 và C ++ 03 sử dụng thuật ngữ khác nhau khi mô tả cách thức ACđược xây dựng, kết quả là giống nhau trong cả hai tiêu chuẩn. Chỉ có struct Bkết quả trong hành vi khác nhau.
Michael Burr

1
Điều tôi muốn nói là nếu bạn đổi C thành struct C { C() : m() {}; ~C(); B m; };, thì bạn sẽ m.mlà 0. Nhưng nếu nó sẽ khởi tạo mặc định mnhư bạn nói C ++ 03, thì m.mnó sẽ không được khởi tạo như trong C ++ 98.
Julian Schaub - litb

1
Nhận xét thú vị khác về việc xử lý MSVC của tính năng này: stackoverflow.com/questions/3931312/ trên
nobar

Việc khởi tạo nào diễn ra khi bạn khai báo kiểu của bạn là biến cục bộ, tức là tại ngăn xếp?
André Puel

87

C ++ 03 Tiêu chuẩn 8,5 / 5:

Để không khởi tạo một đối tượng thuộc loại T có nghĩa là:
- nếu T là loại vô hướng (3.9), thì đối tượng được đặt thành giá trị 0 (không) được chuyển đổi thành T;
- nếu T là loại lớp không liên kết, mỗi thành viên dữ liệu phi tập trung và mỗi tiểu dự án lớp cơ sở là không khởi tạo;
- nếu T là loại kết hợp, thành viên dữ liệu được đặt tên đầu tiên của đối tượng sẽ không được khởi tạo;
- nếu T là một kiểu mảng, mỗi phần tử được khởi tạo bằng không;
- nếu T là loại tham chiếu, không có khởi tạo nào được thực hiện.

Để khởi tạo mặc định một đối tượng thuộc loại T có nghĩa là:
- nếu T là loại lớp không phải POD (mệnh đề 9), hàm tạo mặc định cho T được gọi (và khởi tạo được định dạng sai nếu T không có hàm tạo mặc định có thể truy cập được);
- nếu T là một kiểu mảng, mỗi phần tử được khởi tạo mặc định;
- nếu không, đối tượng là không khởi tạo.

Để khởi tạo giá trị một đối tượng thuộc loại T có nghĩa là:
- nếu T là loại lớp (mệnh đề 9) với hàm tạo do người dùng khai báo (12.1), thì hàm tạo mặc định cho T được gọi (và khởi tạo không được định dạng nếu T không có hàm tạo mặc định có thể truy cập);
- nếu T là loại lớp không liên kết mà không có hàm tạo do người dùng khai báo, thì mọi thành viên dữ liệu không tĩnh và thành phần lớp cơ sở của T đều được khởi tạo giá trị;
- nếu T là một kiểu mảng, thì mỗi phần tử được khởi tạo giá trị;
- nếu không, đối tượng là không khởi tạo

Một chương trình yêu cầu khởi tạo mặc định hoặc khởi tạo giá trị của một thực thể của kiểu tham chiếu không được định dạng đúng. Nếu T là loại đủ điều kiện cv, phiên bản không đủ tiêu chuẩn của cv được sử dụng cho các định nghĩa về khởi tạo không, khởi tạo mặc định và khởi tạo giá trị.


17
Điều này có thể đã lỗi thời đối với C ++ 11. cppreference.com tuyên bố rằng khởi tạo mặc định không khởi tạo thành viên bằng không (chỉ khởi tạo giá trị mới có).
Alexei Sholik

3
@android nêu lên một điểm quan trọng mà tôi không thấy câu trả lời ở nơi nào khác, vì vậy tôi đã đưa ra một câu hỏi mới. stackoverflow.com/questions/22233148/ Lời
Adrian McCarthy
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.