Xây dựng là một chủ đề khá khó trong C++. Câu trả lời đơn giản là tùy thuộc vào. Cho dù Foo được khởi tạo hay không phụ thuộc vào định nghĩa của Foo. Về câu hỏi thứ hai: làm thế nào để làm cho Bar khởi tạo Foo: danh sách khởi tạo là câu trả lời.
Mặc dù sự đồng thuận chung là Foo sẽ được khởi tạo mặc định bởi hàm tạo mặc định ngầm (trình biên dịch được tạo), nhưng không cần phải giữ đúng.
Nếu Foo không có hàm tạo mặc định do người dùng xác định thì Foo sẽ không được khởi tạo. Để được chính xác hơn: mỗi thành viên của Bar hoặc Foo thiếu một người sử dụng định nghĩa constructor mặc định sẽ được uninitialized bởi trình biên dịch tạo ra constructor mặc định của Bar:
class Foo {
int x;
public:
void dump() { std::cout << x << std::endl; }
void set() { x = 5; }
};
class Bar {
Foo x;
public:
void dump() { x.dump(); }
void set() { x.set(); }
};
class Bar2
{
Foo x;
public:
Bar2() : Foo() {}
void dump() { x.dump(); }
void set() { x.set(); }
};
template <typename T>
void test_internal() {
T x;
x.dump();
x.set();
x.dump();
}
template <typename T>
void test() {
test_internal<T>();
test_internal<T>();
}
int main()
{
test<Foo>(); // prints ??, 5, 5, 5, where ?? is a random number, possibly 0
test<Bar>(); // prints ??, 5, 5, 5
test<Bar2>(); // prints 0, 5, 0, 5
}
Bây giờ, nếu Foo có một người sử dụng định nghĩa constructor sau đó nó sẽ được khởi tạo luôn, bất kể Bar có hay không một người tạo khởi tạo. Nếu Bar có một hàm tạo do người dùng định nghĩa để gọi một cách rõ ràng hàm tạo (có thể được xác định ngầm định) của Foo, thì thực tế Foo sẽ được khởi tạo. Nếu danh sách khởi tạo của Bar không gọi hàm tạo Foo thì nó sẽ tương đương với trường hợp Bar không có hàm tạo do người dùng định nghĩa.
Mã thử nghiệm có thể cần một số giải thích. Chúng tôi quan tâm đến việc trình biên dịch có khởi tạo biến mà không có mã người dùng thực sự gọi hàm tạo hay không. Chúng tôi muốn kiểm tra xem đối tượng có được khởi tạo hay không. Bây giờ nếu chúng ta chỉ cần tạo một đối tượng trong một hàm, nó có thể xảy ra để đánh một vị trí bộ nhớ không bị ảnh hưởng và đã chứa các số 0. Chúng tôi muốn phân biệt sự may mắn từ thành công, vì vậy chúng tôi xác định một biến trong một hàm và gọi hàm hai lần. Trong lần chạy đầu tiên, nó sẽ in nội dung bộ nhớ và bắt buộc thay đổi. Trong cuộc gọi thứ hai đến hàm, khi dấu vết ngăn xếp giống nhau, biến sẽ được giữ chính xác ở cùng vị trí bộ nhớ. Nếu nó được khởi tạo, nó sẽ được đặt thành 0, nếu không nó sẽ giữ cùng một giá trị biến cũ ở chính xác cùng một vị trí.
Trong mỗi lần chạy thử, giá trị đầu tiên được in là giá trị khởi tạo (nếu nó đã được khởi tạo) hoặc giá trị ở vị trí bộ nhớ đó, trong một số trường hợp là 0. Giá trị thứ hai chỉ là một thử nghiệm token đại diện cho giá trị tại vị trí bộ nhớ sau khi thay đổi nó theo cách thủ công. Giá trị thứ ba xuất phát từ lần chạy thứ hai của hàm. Nếu biến được khởi tạo, nó sẽ giảm về 0. Nếu đối tượng không được khởi tạo, bộ nhớ của nó sẽ giữ nội dung cũ.
Tôi quen thuộc với C++ và tôi vẫn có một số nghi ngờ về mức độ này thường xuyên. Hơn nữa, hầu hết các câu trả lời đều nói rõ rằng hàm tạo mặc định Foo sẽ được gọi và thực tế là nó phụ thuộc vào định nghĩa của chính Foo. Nó là một người sử dụng cung cấp hoặc ngầm định constructor? Nó có thuộc tính thành viên riêng nào không? Khởi tạo trong C++ không đơn giản. –
Khá buồn cười rằng @xtofl yêu cầu người đăng ký xóa 'Tôi quen thuộc với C++' ... Có lẽ hầu hết mọi người không phải là 'quen thuộc' với C++ khi gần như tất cả các câu trả lời đều sai. Trong thực tế khởi tạo là khó, một số người trả lời đã chứng minh kiến thức C++ của họ @JaredPar, @dirkgently, @David Thornley và thất bại. –