Thứ tự của các lệnh gọi hàm tạo thành viên và hàm hủy


120

Ôi các bậc thầy C ++, tôi tìm kiếm sự thông thái của bạn. Nói chuyện chuẩn với tôi và cho tôi biết nếu C ++ đảm bảo rằng chương trình sau:

#include <iostream>
using namespace std;

struct A
{
    A() { cout << "A::A" << endl; }
    ~A() { cout << "A::~" << endl; }
};

struct B
{
    B() { cout << "B::B" << endl; }
    ~B() { cout << "B::~" << endl; }
};

struct C
{
    C() { cout << "C::C" << endl; }
    ~C() { cout << "C::~" << endl; }
};

struct Aggregate
{
    A a;
    B b;
    C c;
};

int main()
{
    Aggregate a;
    return 0;
}

sẽ luôn sản xuất

A::A
B::B
C::C
C::~
B::~
A::~

Nói cách khác, các thành viên có đảm bảo được khởi tạo theo thứ tự khai báo và bị hủy theo thứ tự ngược lại không?


8
Đây là một nguyên nhân khá phổ biến gây ra các lỗi nhỏ khi các lớp phát triển quá lớn và không hoạt động. Khi bạn có 50 thành viên dữ liệu và rất nhiều thành viên trong số chúng được khởi tạo trong danh sách bộ khởi tạo intializer, có thể dễ dàng giả sử thứ tự xây dựng là thứ tự trong danh sách bộ khởi tạo. Rốt cuộc, những người viết mã đã sắp xếp danh sách cẩn thận ... phải không?
Permaquid

Câu trả lời:


139

Nói cách khác, các thành viên có đảm bảo được khởi tạo theo thứ tự khai báo và bị hủy theo thứ tự ngược lại không?

Có cho cả hai. Xem 12.6.2

6 Quá trình khởi tạo sẽ tiến hành theo trình tự sau:

  • Đầu tiên, và chỉ đối với phương thức khởi tạo của lớp dẫn xuất nhất như được mô tả bên dưới, các lớp cơ sở ảo sẽ được khởi tạo theo thứ tự xuất hiện trên đường đi ngang từ trái sang phải theo chiều sâu từ trái sang phải của đồ thị xoay chiều có hướng của các lớp cơ sở, trong đó “left -to-right ”là thứ tự xuất hiện của các tên lớp cơ sở trong danh sách base-specifier-list của lớp dẫn xuất.

  • Sau đó, các lớp cơ sở trực tiếp sẽ được khởi tạo theo thứ tự khai báo khi chúng xuất hiện trong danh sách cơ sở chỉ định (bất kể thứ tự của các trình khởi tạo ghi nhớ).

  • Sau đó, các thành viên dữ liệu không tĩnh sẽ được khởi tạo theo thứ tự mà chúng đã được khai báo trong định nghĩa lớp (một lần nữa bất kể thứ tự của các trình khởi tạo mem).

  • Cuối cùng, câu lệnh ghép của phần thân hàm tạo được thực thi. [Lưu ý: thứ tự khai báo được yêu cầu để đảm bảo rằng các subobject cơ sở và thành viên bị hủy theo thứ tự khởi tạo ngược lại. —Gửi ghi chú]


2
Nếu tôi nhớ không lầm, có cho cả hai ... Hãy coi nó như một ngăn xếp. Lần đẩy đầu tiên, lần cuối cùng bật lên. Vì vậy, khi khởi tạo phiên bản đầu tiên của bạn, nó được đẩy vào bộ nhớ theo thứ tự của một ngăn xếp. Sau đó, thứ hai được đẩy qua, thứ ba trên thứ hai và cứ tiếp tục như vậy. Sau đó, khi hủy các cá thể của bạn, chương trình sẽ tìm kiếm cái đầu tiên để phá hủy, cái cuối cùng đã được đẩy. Nhưng tôi có thể giải thích sai theo cách này, nhưng đó là cách tôi học được khi làm C / C ++ và ASM.
Will Marcouiller

29

Có, họ là (thành viên không tĩnh). Xem 12.6.2 / 5 để khởi tạo (xây dựng) và 12.4 / 6 để phá hủy.


10

Có, các tiêu chuẩn đảm bảo các đối tượng bị hủy theo thứ tự ngược lại mà chúng đã được tạo. Lý do là một đối tượng có thể sử dụng đối tượng khác, do đó phụ thuộc vào nó. Xem xét:

struct A { };

struct B {
 A &a;
 B(A& a) : a(a) { }
};

int main() {
    A a;
    B b(a);
}

Nếu ađược hủy trước bđó thì bsẽ giữ một tham chiếu thành viên không hợp lệ. Bằng cách phá hủy các đối tượng theo thứ tự ngược lại mà chúng được tạo ra, chúng tôi đảm bảo việc phá hủy chính xác.


Tôi chưa bao giờ thực sự nghĩ rằng quy tắc này cũng áp dụng cho lệnh tiêu diệt của các thành viên phạm vi!
yano

6

Có và có. Thứ tự phá hủy luôn ngược lại với thứ tự xây dựng, đối với các biến thành viên.

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.