Lỗi khởi tạo mặc định, giá trị và không


89

Tôi rất bối rối về giá trị- & mặc định- & không khởi tạo. và đặc biệt là khi họ bắt đầu sử dụng các tiêu chuẩn khác nhau C ++ 03C ++ 11 (và C ++ 14 ).

Tôi đang trích dẫn và cố gắng mở rộng một câu trả lời thực sự tốt Value- / Default- / Zero- Init C ++ 98C ++ 03 ở đây để làm cho nó tổng quát hơn vì nó sẽ giúp rất nhiều người dùng nếu ai đó có thể giúp điền vào những khoảng trống cần thiết để có cái nhìn tổng quan tốt về điều gì xảy ra khi nào?

Tóm lại, toàn bộ thông tin chi tiết bằng các ví dụ:

Đôi khi bộ nhớ được trả về bởi toán tử mới sẽ được khởi tạo và đôi khi nó sẽ không phụ thuộc vào việc loại bạn mới tải lên là POD (dữ liệu cũ thuần túy) hay là một lớp có chứa các thành viên POD và đang sử dụng phương thức khởi 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: zero-mặc định-khởi
  • Trong C ++ 2003, kiểu khởi tạo thứ 3, khởi tạo giá trị đã được thêm vào.
  • Trong C ++ 2011 / C ++ 2014 chỉ khởi tạo danh sách được thêm vào và các quy tắc cho khởi tạo value- / default- / zero đã thay đổi một chút.

Giả định:

struct A { int m; };                     
struct B { ~B(); int m; };               
struct C { C() : m(){}; ~C(); int m; };  
struct D { D(){}; int m; };             
struct E { E() = default; int m;}; /** only possible in c++11/14 */  
struct F {F(); int m;};  F::F() = default; /** only possible in c++11/14 */

Trong trình biên dịch C ++ 98, điều sau sẽ xảy ra :

  • new A - giá trị không xác định ( Alà POD)
  • new A()- không khởi tạo
  • new B - cấu trúc mặc định ( B::mchưa được khởi tạo, Bkhông phải POD)
  • new B()- cấu trúc mặc định ( B::mchưa được khởi tạo)
  • new C - cấu trúc mặc định ( C::mkhông được khởi tạo, Ckhông phải là POD)
  • new C()- cấu trúc mặc định ( C::mđược khởi tạo bằng 0)
  • new D - cấu trúc mặc định ( D::mchưa được khởi tạo, Dkhông phải POD)
  • new D()- cấu trúc mặc định? ( D::mchưa được khởi tạo)

Trong trình biên dịch tuân theo C ++ 03, mọi thứ sẽ hoạt động như vậy:

  • new A - giá trị không xác định ( Alà POD)
  • new A() - value-initialize A, là không khởi tạo vì nó là POD.
  • new B - khởi tạo mặc định ( B::mkhông khởi tạo, Bkhông phải POD)
  • new B() - giá trị khởi tạo Bkhông khởi tạo tất cả các trường vì ctor mặc định của nó là trình biên dịch được tạo ra trái ngược với do người dùng xác định.
  • new C - default-khởi tạo C, gọi ctor mặc định. ( C::mkhông được khởi tạo, Ckhông phải POD)
  • new C() - giá trị khởi tạo C, gọi ctor mặc định. ( C::mđược khởi tạo bằng 0)
  • new D - cấu trúc mặc định ( D::mchưa được khởi tạo, Dkhông phải POD)
  • new D() - giá trị khởi tạo D? , gọi là ctor mặc định ( D::mchưa được khởi tạo)

Giá trị nghiêng và? là những điểm không chắc chắn, vui lòng giúp sửa lỗi này :-)

Trong trình biên dịch tuân theo C ++ 11, mọi thứ sẽ hoạt động như vậy:

??? (xin vui lòng giúp đỡ nếu tôi bắt đầu ở đây, nó vẫn sẽ sai)

Trong một trình biên dịch tuân thủ C ++ 14, mọi thứ sẽ hoạt động như vậy: ??? (Xin vui lòng giúp đỡ nếu tôi bắt đầu ở đây, nó vẫn sẽ sai )

  • new A - default-khởi tạo A, gen biên dịch. ctor, (leavs A::muninitialized) ( Alà POD)

  • new A() - giá trị khởi tạo A, là không khởi tạo kể từ 2. điểm trong [dcl.init] / 8

  • new B - default-khởi tạo B, gen biên dịch. ctor, (leavs B::muninitialized) ( Bkhông phải POD)

  • new B() - giá trị khởi tạo Bkhông khởi tạo tất cả các trường vì ctor mặc định của nó là trình biên dịch được tạo ra trái ngược với do người dùng xác định.

  • new C - default-khởi tạo C, gọi ctor mặc định. ( C::mkhông được khởi tạo, Ckhông phải POD)

  • new C() - giá trị khởi tạo C, gọi ctor mặc định. ( C::mđược khởi tạo bằng 0)

  • new D - mặc định khởi tạo D( D::mchưa được khởi tạo, Dkhông phải POD)

  • new D() - giá trị khởi tạo D, gọi ctor mặc định ( D::mchưa được khởi tạo)

  • new E - default-khởi tạo E, gọi comp. gen. ctor. ( E::mchưa được khởi tạo, E không phải POD)

  • new E() - giá trị khởi tạo E, khởi tạo bằng không Ekể từ 2 điểm trong [dcl.init] / 8 )

  • new F - default-khởi tạo F, gọi comp. gen. ctor. ( F::mchưa được khởi tạo, Fkhông phải POD)

  • new F() - value-khởi tạo F, khởi tạo mặc định F kể từ 1. point trong [dcl.init] / 8 ( Fhàm ctor do người dùng cung cấp nếu nó do người dùng khai báo và không được mặc định rõ ràng hoặc bị xóa trên khai báo đầu tiên của nó. Liên kết )


có một lời giải thích tốt về nó ở đây: en.cppreference.com/w/cpp/language/default_constructor
Richard Hodges

1
Theo như tôi có thể nói, chỉ có sự khác biệt giữa C ++ 98 và C ++ 03 trong các ví dụ này. Vấn đề dường như được mô tả trong N1161 (có các bản sửa đổi sau đó của tài liệu đó) và CWG DR # 178 . Từ ngữ cần thay đổi trong C ++ 11 do các tính năng mới và đặc điểm kỹ thuật mới của POD, và nó đã thay đổi một lần nữa trong C ++ 14 do các khiếm khuyết trong từ ngữ C ++ 11, nhưng tác động trong những trường hợp này không thay đổi .
dyp

3
Trong khi nhàm chán, struct D { D() {}; int m; };có thể đáng để đưa vào danh sách của bạn.
Yakk - Adam Nevraumont

Câu trả lời:


24

C ++ 14 chỉ định khởi tạo các đối tượng được tạo bằng new[expr.new] / 17 ([expr.new] / 15 trong C ++ 11 và ghi chú không phải là ghi chú mà là văn bản quy chuẩn hồi đó):

Một biểu thức mới tạo một đối tượng kiểu sẽ Tkhởi tạo đối tượng đó như sau:

  • Nếu trình khởi tạo mới bị bỏ qua, đối tượng sẽ được khởi tạo mặc định (8.5). [ Lưu ý: Nếu không thực hiện khởi tạo, đối tượng có giá trị không xác định. - ghi chú cuối ]
  • Nếu không, trình khởi tạo mới được diễn giải theo các quy tắc khởi tạo của 8.5 để khởi tạo trực tiếp .

Khởi tạo mặc định được định nghĩa trong [dcl.init] / 7 (/ 6 trong C ++ 11 và bản thân từ ngữ cũng có tác dụng tương tự):

Để khởi tạo mặc định một đối tượng kiểu Tnghĩa là:

  • nếu Tlà một loại lớp (có thể là cv đủ điều kiện) (Khoản 9), thì hàm tạo mặc định (12.1) cho Tđược gọi (và quá trình khởi tạo không hợp lệ nếu Tkhông có hàm tạo mặc định hoặc giải quyết quá tải (13.3) dẫn đến không rõ ràng hoặc trong một chức năng bị xóa hoặc không thể truy cập được từ ngữ cảnh khởi tạo);
  • nếu Tlà một kiểu mảng, mỗi phần tử được khởi tạo mặc định ;
  • nếu không, không có khởi tạo nào được thực hiện.

Như vậy

  • new Achỉ khiến hàm tạo Amặc định của được gọi, không khởi tạo m. Giá trị không xác định. Nên giống nhau cho new B.
  • new A() được diễn giải theo [dcl.init] / 11 (/ 10 trong C ++ 11):

    Một đối tượng có bộ khởi tạo là một tập hợp rỗng của dấu ngoặc đơn, tức là (), sẽ được khởi tạo giá trị.

    Và bây giờ hãy xem xét [dcl.init] / 8 (/ 7 trong C ++ 11 †):

    Để khởi tạo giá trị một đối tượng kiểu Tcó nghĩa là:

    • nếu Tlà một loại lớp (có thể là cv đủ điều kiện) (Khoản 9) không có hàm tạo mặc định (12.1) hoặc một hàm tạo mặc định do người dùng cung cấp hoặc bị xóa, thì đối tượng được khởi tạo mặc định;
    • nếu Tlà một loại lớp (có thể là cv đủ điều kiện) mà không có hàm tạo mặc định do người dùng cung cấp hoặc bị xóa, thì đối tượng được khởi tạo bằng 0 và các ràng buộc ngữ nghĩa đối với khởi tạo mặc định được kiểm tra và nếu T có hàm tạo mặc định không tầm thường, đối tượng được khởi tạo mặc định;
    • nếu Tlà 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 không được khởi tạo.

    Do đó new A()sẽ không khởi tạo m. Và điều này phải tương đương với AB.

  • new Cnew C()sẽ mặc định khởi tạo lại đối tượng, vì áp dụng dấu đầu dòng đầu tiên từ dấu ngoặc kép cuối cùng (C có một hàm tạo mặc định do người dùng cung cấp!). Nhưng, rõ ràng, bây giờ mđược khởi tạo trong hàm tạo trong cả hai trường hợp.


† Chà, đoạn này có cách diễn đạt hơi khác trong C ++ 11, điều này không làm thay đổi kết quả:

Để khởi tạo giá trị một đối tượng kiểu Tcó nghĩa là:

  • nếu Tlà một loại lớp (có thể đủ điều kiện cv) (Khoản 9) với một hàm tạo do người dùng cung cấp (12.1), thì hàm tạo mặc định cho T được gọi (và quá trình khởi tạo không hợp lệ nếu T không có hàm tạo mặc định có thể truy cập được);
  • nếu Tlà một loại lớp không liên hiệp (có thể là cv đủ điều kiện) không có hàm tạo do người dùng cung cấp, thì đối tượng được khởi tạo bằng 0 và nếu Thàm tạo mặc định được khai báo ngầm định là không tầm thường, thì hàm tạo đó được gọi.
  • nếu Tlà 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 không được khởi tạo.

@Gabriel không thực sự.
Columbo

ah, do đó bạn đang nói chủ yếu về c ++ 14 và tài liệu tham khảo cho c ++ 11 được đưa ra trong dấu ngoặc
Gabriel

@Gabriel Đúng. Ý tôi là, C ++ 14 là tiêu chuẩn mới nhất, vì vậy đó là tiêu chuẩn hàng đầu.
Columbo

1
Điều khó chịu khi cố gắng theo dõi các quy tắc khởi tạo giữa các tiêu chuẩn là rất nhiều thay đổi (hầu hết? Tất cả?) Giữa các tiêu chuẩn C ++ 14 và C ++ 11 đã xuất bản đã xảy ra thông qua DRs và trên thực tế C ++ 11 cũng vậy. . Và sau đó cũng có các DR sau C ++ 14 ...
TC

@Columbo Tôi vẫn không hiểu tại sao lại struct A { int m; }; struct C { C() : m(){}; int m; };tạo ra các kết quả khác nhau và nguyên nhân khiến m trong A được khởi tạo ngay từ đầu. Tôi đã mở một chủ đề dành riêng cho thử nghiệm mà tôi đã làm và tôi sẽ đánh giá cao ý kiến ​​đóng góp của bạn ở đó để làm rõ vấn đề. Cảm ơn stackoverflow.com/questions/45290121/…
darkThoughts

12

Câu trả lời sau mở rộng câu trả lời https://stackoverflow.com/a/620402/977038 sẽ dùng làm tham chiếu cho C ++ 98 và C ++ 03

Trích dẫn câu trả lời

  1. Trong C ++ 1998 có 2 kiểu khởi tạo: zero và default
  2. Trong C ++ 2003, kiểu khởi tạo thứ 3, khởi tạo giá trị đã được thêm vào.

C ++ 11 (Tham chiếu đến n3242)

Khởi tạo

8.5 Bộ khởi tạo [dcl.init] chỉ định rằng một biến POD hoặc không phải POD có thể được khởi tạo dưới dạng bộ khởi tạo dấu ngoặc nhọn-hoặc-bằng- có thể là dấu ngoặc nhọn -init-list hoặc bộ khởi tạo-mệnh đề được gọi chung là dấu ngoặc nhọn-hoặc-bằng- khởi tạo hoặc sử dụng (danh sách biểu thức) . Trước C ++ 11, chỉ (danh sách biểu thức) hoặc mệnh đề khởi tạo được hỗ trợ mặc dù mệnh đề khởi tạo bị hạn chế hơn so với những gì chúng ta có trong C ++ 11. Trong C ++ 11, mệnh đề khởi tạo giờ đây hỗ trợ danh sách giằng-init- ngoài với biểu thức gánnhư trong C ++ 03. Ngữ pháp sau đây tóm tắt mệnh đề được hỗ trợ mới, trong đó phần được in đậm mới được thêm vào trong tiêu chuẩn C ++ 11.

khởi tạo:
    dấu ngoặc nhọn-hoặc-bằng-khởi tạo
    (biểu thức-danh sách)
dấu ngoặc nhọn-hoặc-bằng-khởi tạo:
    = bộ khởi tạo-mệnh đề
    giằng-init-danh sách
khởi tạo-mệnh đề:
    phép gán-biểu thức
    giằng-init-danh sách
khởi tạo-danh sách: bộ
    khởi tạo-mệnh đề ... opt
    initializer-list, khởi tạo-mệnh đề ... opt **
Braced-init-list:
    {initializer-list, opt}
    {}

Khởi tạo

Giống như C ++ 03, C ++ 11 vẫn hỗ trợ ba hình thức khởi tạo


Ghi chú

Phần được tô đậm đã được thêm vào trong C ++ 11 và phần được đánh dấu đã bị xóa khỏi C ++ 11.

  1. Loại khởi tạo: 8.5.5 [dcl.init] _zero-initialize_

Thực hiện trong các trường hợp sau

  • Các đối tượng có thời lượng lưu trữ tĩnh hoặc chuỗi không được khởi tạo
  • Nếu có ít phần tử khởi tạo hơn số phần tử mảng, thì mỗi phần tử không được khởi tạo rõ ràng sẽ được khởi tạo bằng 0
  • Trong quá trình khởi tạo giá trị , nếu T là một loại lớp không liên hợp (có thể đủ tiêu chuẩn cv) mà không có hàm tạo do người dùng cung cấp, thì đối tượng được khởi tạo bằng 0.

Để khởi tạo không đối tượng hoặc tham chiếu kiểu T có nghĩa là:

  • nếu T là một kiểu vô hướng (3.9), đối tượng được đặt thành giá trị 0 (không), được coi là một biểu thức hằng tích phân , được chuyển thành T;
  • nếu T là kiểu lớp không liên hiệp (có thể đủ tiêu chuẩn cv) , thì mỗi thành viên dữ liệu không tĩnh và mỗi đối tượng lớp cơ sở được khởi tạo bằng 0 và phần đệm được khởi tạo thành bit 0;
  • nếu T là kiểu liên hiệp (có thể đủ điều kiện cv) , thành viên dữ liệu có tên không tĩnh đầu tiên của đối tượng được khởi tạo bằng 0 và phần đệm được khởi tạo thành bit 0;
  • nếu T là một kiểu mảng, mỗi phần tử được khởi tạo bằng 0;
  • nếu T là một kiểu tham chiếu, không có khởi tạo nào được thực hiện.

2. Loại khởi tạo: 8.5.6 [dcl.init] _default-khởi tạo_

Thực hiện trong các trường hợp sau

  • Nếu trình khởi tạo mới bị bỏ qua, đối tượng được khởi tạo mặc định; nếu không có khởi tạo nào được thực hiện, đối tượng có giá trị không xác định.
  • Nếu không có trình khởi tạo nào được chỉ định cho một đối tượng, đối tượng sẽ được khởi tạo mặc định, ngoại trừ Đối tượng có thời lượng lưu trữ tĩnh hoặc chuỗi
  • Khi một lớp cơ sở hoặc một thành viên dữ liệu không tĩnh không được đề cập trong danh sách bộ khởi tạo phương thức khởi tạo và phương thức khởi tạo đó được gọi.

Để khởi tạo mặc định một đối tượng kiểu T có nghĩa là:

  • nếu T là kiểu lớp không POD (có thể đủ tiêu chuẩn cv) (Điều 9), thì hàm tạo mặc định cho T được gọi (và quá trình khởi tạo không được hình thành 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, không có khởi tạo nào được thực hiện.

Lưu ý Cho đến khi C ++ 11, chỉ các loại lớp không phải POD có thời lượng lưu trữ tự động mới được coi là khởi tạo mặc định khi không sử dụng trình khởi tạo.


3. Loại khởi tạo: 8.5.7 [dcl.init] _value-initialize_

  1. Khi một đối tượng (tạm thời không tên, biến có tên, thời lượng lưu trữ động hoặc thành viên dữ liệu không tĩnh) có bộ khởi tạo là một tập hợp rỗng của dấu ngoặc đơn, tức là () hoặc dấu ngoặc nhọn {}

Để khởi tạo giá trị một đối tượng kiểu T có nghĩa là:

  • nếu T là một loại lớp (có thể đủ tiêu chuẩn cv) (Điều 9) với một hàm tạo do người dùng cung cấp (12.1), thì hàm tạo mặc định cho T được gọi (và quá trình khởi tạo không đúng 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 loại lớp không liên hiệp (có thể đủ tiêu chuẩn cv) không có hàm tạo do người dùng cung cấp, thì mọi thành phầ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ị; thì đối tượng được khởi tạo bằng 0 và, nếu hàm tạo mặc định được khai báo ngầm của T là không tầm thường, thì hàm tạo đó được gọi.
  • 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 không được khởi tạo.

Vì vậy, để tóm tắt

Lưu ý Trích dẫn có liên quan từ tiêu chuẩn được đánh dấu bằng chữ đậm

  • new A: default-initializes (để lại A :: m chưa được khởi tạo)
  • new A (): Không khởi tạo A, vì giá trị được khởi tạo ứng viên không có hàm tạo mặc định do người dùng cung cấp hoặc bị xóa. nếu T là một loại lớp không liên hợp (có thể đủ điều kiện cv) không có hàm tạo do người dùng cung cấp, thì đối tượng được khởi tạo bằng 0 và nếu hàm tạo mặc định được khai báo rõ ràng của T là không tầm thường, thì hàm tạo đó được gọi.
  • mới B: mặc định-khởi tạo (để lại B :: m chưa được khởi tạo)
  • new B (): giá trị khởi tạo B không khởi tạo tất cả các trường; nếu T là một loại lớp (có thể đủ điều kiện cv) (Điều 9) với một hàm tạo do người dùng cung cấp (12.1), thì hàm tạo mặc định cho T được gọi
  • new C: default-khởi tạo C, gọi ctor mặc định. nếu T là một loại lớp (có thể đủ điều kiện cv) (Khoản 9), hàm tạo mặc định cho T được gọi , Hơn nữa, Nếu bộ khởi tạo mới bị bỏ qua, đối tượng được khởi tạo mặc định
  • new C (): giá trị khởi tạo C, gọi là ctor mặc định. nếu T là một loại lớp (có thể đủ tiêu chuẩn cv) (Điều 9) với một hàm tạo do người dùng cung cấp (12.1), thì hàm tạo mặc định cho T được gọi. Hơn nữa, một đối tượng có bộ khởi tạo là một tập hợp rỗng của dấu ngoặc đơn, tức là, (), sẽ được khởi tạo giá trị

0

Tôi có thể xác nhận rằng trong C ++ 11, mọi thứ được đề cập trong câu hỏi trong C ++ 14 là đúng, ít nhất là theo các triển khai trình biên dịch.

Để xác minh điều này, tôi đã thêm mã sau vào bộ thử nghiệm của mình . Tôi đã thử nghiệm với -std=c++11 -O3GCC 7.4.0, GCC 5.4.0, Clang 10.0.1 và VS 2017 và tất cả các thử nghiệm bên dưới đều vượt qua.

#include <gtest/gtest.h>
#include <memory>

struct A { int m;                    };
struct B { int m;            ~B(){}; };
struct C { int m; C():m(){}; ~C(){}; };
struct D { int m; D(){};             };
struct E { int m; E() = default;     };
struct F { int m; F();               }; F::F() = default;

// We use this macro to fill stack memory with something else than 0.
// Subsequent calls to EXPECT_NE(a.m, 0) are undefined behavior in theory, but
// pass in practice, and help illustrate that `a.m` is indeed not initialized
// to zero. Note that we initially tried the more aggressive test
// EXPECT_EQ(a.m, 42), but it didn't pass on all compilers (a.m wasn't equal to
// 42, but was still equal to some garbage value, not zero).
//
#define FILL { int m = 42; EXPECT_EQ(m, 42); }

// We use this macro to fill heap memory with something else than 0, before
// doing a placement new at that same exact location. Subsequent calls to
// EXPECT_EQ(a->m, 42) are undefined behavior in theory, but pass in practice,
// and help illustrate that `a->m` is indeed not initialized to zero.
//
#define FILLH(b) std::unique_ptr<int> bp(new int(42)); int* b = bp.get(); EXPECT_EQ(*b, 42)

TEST(TestZero, StackDefaultInitialization)
{
    { FILL; A a; EXPECT_NE(a.m, 0); } // UB!
    { FILL; B a; EXPECT_NE(a.m, 0); } // UB!
    { FILL; C a; EXPECT_EQ(a.m, 0); }
    { FILL; D a; EXPECT_NE(a.m, 0); } // UB!
    { FILL; E a; EXPECT_NE(a.m, 0); } // UB!
    { FILL; F a; EXPECT_NE(a.m, 0); } // UB!
}

TEST(TestZero, StackValueInitialization)
{
    { FILL; A a = A(); EXPECT_EQ(a.m, 0); }
    { FILL; B a = B(); EXPECT_EQ(a.m, 0); }
    { FILL; C a = C(); EXPECT_EQ(a.m, 0); }
    { FILL; D a = D(); EXPECT_NE(a.m, 0); } // UB!
    { FILL; E a = E(); EXPECT_EQ(a.m, 0); }
    { FILL; F a = F(); EXPECT_NE(a.m, 0); } // UB!
}

TEST(TestZero, StackListInitialization)
{
    { FILL; A a{}; EXPECT_EQ(a.m, 0); }
    { FILL; B a{}; EXPECT_EQ(a.m, 0); }
    { FILL; C a{}; EXPECT_EQ(a.m, 0); }
    { FILL; D a{}; EXPECT_NE(a.m, 0); } // UB!
    { FILL; E a{}; EXPECT_EQ(a.m, 0); }
    { FILL; F a{}; EXPECT_NE(a.m, 0); } // UB!
}

TEST(TestZero, HeapDefaultInitialization)
{
    { FILLH(b); A* a = new (b) A; EXPECT_EQ(a->m, 42); } // ~UB
    { FILLH(b); B* a = new (b) B; EXPECT_EQ(a->m, 42); } // ~UB
    { FILLH(b); C* a = new (b) C; EXPECT_EQ(a->m, 0);  }
    { FILLH(b); D* a = new (b) D; EXPECT_EQ(a->m, 42); } // ~UB
    { FILLH(b); E* a = new (b) E; EXPECT_EQ(a->m, 42); } // ~UB
    { FILLH(b); F* a = new (b) F; EXPECT_EQ(a->m, 42); } // ~UB
}

TEST(TestZero, HeapValueInitialization)
{
    { FILLH(b); A* a = new (b) A(); EXPECT_EQ(a->m, 0);  }
    { FILLH(b); B* a = new (b) B(); EXPECT_EQ(a->m, 0);  }
    { FILLH(b); C* a = new (b) C(); EXPECT_EQ(a->m, 0);  }
    { FILLH(b); D* a = new (b) D(); EXPECT_EQ(a->m, 42); } // ~UB
    { FILLH(b); E* a = new (b) E(); EXPECT_EQ(a->m, 0);  }
    { FILLH(b); F* a = new (b) F(); EXPECT_EQ(a->m, 42); } // ~UB
}

TEST(TestZero, HeapListInitialization)
{
    { FILLH(b); A* a = new (b) A{}; EXPECT_EQ(a->m, 0);  }
    { FILLH(b); B* a = new (b) B{}; EXPECT_EQ(a->m, 0);  }
    { FILLH(b); C* a = new (b) C{}; EXPECT_EQ(a->m, 0);  }
    { FILLH(b); D* a = new (b) D{}; EXPECT_EQ(a->m, 42); } // ~UB
    { FILLH(b); E* a = new (b) E{}; EXPECT_EQ(a->m, 0);  }
    { FILLH(b); F* a = new (b) F{}; EXPECT_EQ(a->m, 42); } // ~UB
}

int main(int argc, char **argv)
{
    ::testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

Những nơi UB!được đề cập là hành vi không xác định và hành vi thực tế có thể phụ thuộc vào nhiều yếu tố ( a.mcó thể bằng 42, 0 hoặc một số rác khác). Những nơi ~UBđược đề cập cũng là hành vi không xác định về lý thuyết, nhưng trên thực tế, do sử dụng một vị trí mới, nó rất khó có khả năng a->mbằng với bất kỳ điều gì khác hơn 42.

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.