Hàm tạo C++ với tham số con trỏ

Constructor giống như “hàm init”. Họ biến một đống bit tùy ý thành một vật thể sống. Tối thiểu họ khởi tạo các trường được sử dụng nội bộ. Họ cũng có thể phân bổ tài nguyên (bộ nhớ, tệp, semaphores, ổ cắm, v.v.)

Show

“ctor” là từ viết tắt điển hình của hàm tạo

Có sự khác biệt nào giữa class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 0 và class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 1 không?

Một sự khác biệt lớn

Giả sử rằng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
2 là tên của một số lớp. Sau đó, hàm
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
3 khai báo một đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
2 cục bộ có tên là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5

void f()
{
  List x;     // Local object named x (of class List)
  // ...
}

Nhưng hàm

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
6 khai báo một hàm có tên là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
7 trả về một
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
2

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
0

Một hàm tạo của một lớp có thể gọi một hàm tạo khác của cùng lớp để khởi tạo đối tượng class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 9 không?

Câu trả lời dưới đây áp dụng cho C++ cổ điển (trước 11 tuổi). bao gồm tính năng C++ 11 của các hàm tạo gọi các hàm tạo cùng loại

Không

Hãy làm một ví dụ. Giả sử bạn muốn hàm tạo của bạn

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
20 gọi một hàm tạo khác của cùng một lớp, chẳng hạn như
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
21, để
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
21 đó sẽ giúp khởi tạo đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9. Thật không may, không có cách nào để làm điều này trong Classic C++

Một số người làm điều đó anyway. Thật không may, nó không làm những gì họ muốn. Ví dụ, dòng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
24 không gọi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
21 trên đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9. Thay vào đó, nó gọi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
21 để khởi tạo một đối tượng cục bộ, tạm thời (không phải
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9), sau đó nó ngay lập tức hủy đối tượng tạm thời đó khi điều khiển chuyển qua
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
29

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
2

Đôi khi bạn có thể kết hợp hai hàm tạo thông qua một tham số mặc định

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
3

Nếu điều đó không làm việc, e. g. , nếu không có tham số mặc định phù hợp kết hợp hai hàm tạo, đôi khi bạn có thể chia sẻ mã chung của chúng trong hàm thành viên

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
40 riêng tư

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5

BTW KHÔNG cố gắng đạt được điều này thông qua. Một số người nghĩ rằng họ có thể nói

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
41 trong phần thân của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
20. Tuy nhiên đó là xấu, xấu, xấu. Xin đừng viết thư cho tôi và nói với tôi rằng nó dường như hoạt động trên phiên bản cụ thể của trình biên dịch cụ thể của bạn; . Những người xây dựng thực hiện một loạt những điều kỳ diệu nho nhỏ đằng sau hậu trường, nhưng kỹ thuật tồi tệ đó sẽ ảnh hưởng đến những bit được xây dựng một phần đó. Chỉ cần nói không

Có phải hàm tạo mặc định cho class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 43 luôn là class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 44 không?

Không

Một “hàm tạo mặc định” là một hàm tạo có thể được gọi mà không có đối số. Một ví dụ về điều này là một hàm tạo không có tham số

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
0

Một ví dụ khác về “hàm tạo mặc định” là một ví dụ có thể nhận các đối số, miễn là chúng được cung cấp các giá trị mặc định

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
1

Hàm tạo nào được gọi khi tôi tạo một mảng gồm các đối tượng class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 43?

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 (ngoại trừ như được thảo luận bên dưới)

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}

Nếu lớp của bạn không có , bạn sẽ gặp lỗi thời gian biên dịch khi cố gắng tạo một mảng bằng cú pháp đơn giản ở trên

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
2

Tuy nhiên, ngay cả khi lớp của bạn đã có một hàm tạo mặc định, bạn nên cố gắng sử dụng chứ không phải là một mảng ().

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
48 cho phép bạn quyết định sử dụng bất kỳ hàm tạo nào, không chỉ hàm tạo mặc định

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
4

Mặc dù bạn nên sử dụng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
48 thay vì một mảng, nhưng đôi khi một mảng có thể là điều nên làm và đối với những trường hợp đó, bạn có thể cần đến cú pháp “khởi tạo mảng rõ ràng”. Đây là cách

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
00

Tất nhiên, bạn không cần phải thực hiện

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
000 cho mọi mục nhập — bạn có thể nhập bất kỳ số nào bạn muốn, thậm chí cả tham số hoặc các biến khác

Cuối cùng, để tự khởi tạo các phần tử của mảng. Cảnh báo. no thật la xâu xi. mảng thô không thể thuộc loại

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43, vì vậy bạn sẽ cần một loạt các phép con trỏ để thực hiện những việc như tính toán các phép toán chỉ mục mảng. Cảnh báo. nó phụ thuộc vào trình biên dịch và phần cứng. bạn sẽ cần đảm bảo rằng bộ lưu trữ được căn chỉnh với sự căn chỉnh ít nhất nghiêm ngặt như yêu cầu đối với các đối tượng của lớp
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43. Cảnh báo. thật tẻ nhạt để làm cho nó an toàn ngoại lệ. bạn sẽ cần hủy các phần tử theo cách thủ công, kể cả trong trường hợp khi một ngoại lệ được ném một phần qua vòng lặp gọi hàm tạo. Nhưng nếu bạn thực sự muốn làm điều đó,. (BTW vị trí-mới là phép thuật được sử dụng bên trong
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
48. Sự phức tạp của việc làm đúng mọi thứ là một lý do khác để sử dụng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
48. )

Nhân tiện, tôi đã bao giờ đề cập đến điều đó chưa?

Các hàm tạo của tôi có nên sử dụng "danh sách khởi tạo" hoặc "chỉ định" không?

danh sách khởi tạo. Trên thực tế, các hàm tạo nên khởi tạo theo quy tắc tất cả các đối tượng thành viên trong danh sách khởi tạo. Một ngoại lệ được thảo luận sâu hơn

Xem không gian này để thảo luận về Khởi tạo thành viên dữ liệu không tĩnh trong C++11

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
01

Xem xét hàm tạo sau khởi tạo đối tượng thành viên

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
006 bằng cách sử dụng danh sách khởi tạo.
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
007bất cứ điều gì_______1008. Lợi ích phổ biến nhất của việc này là cải thiện hiệu suất. Ví dụ: nếu biểu thức anything cùng loại với biến thành viên
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
006, thì kết quả của biểu thức anything được tạo trực tiếp bên trong
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
006 — trình biên dịch không tạo một bản sao riêng của đối tượng. Ngay cả khi các kiểu không giống nhau, trình biên dịch thường có thể thực hiện công việc tốt hơn với các danh sách khởi tạo so với các phép gán

Cách khác (không hiệu quả) để xây dựng hàm tạo là thông qua phép gán, chẳng hạn như. ________ 1011 sao cũng được ________ 1012. Trong trường hợp này, biểu thức anything sẽ tạo ra một đối tượng tạm thời, riêng biệt và đối tượng tạm thời này được chuyển vào toán tử gán của đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
006. Sau đó, đối tượng tạm thời đó bị hủy tại
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
29. Điều đó không hiệu quả

Như thể điều đó chưa đủ tệ, còn có một nguyên nhân khác dẫn đến sự kém hiệu quả khi sử dụng phép gán trong hàm tạo. đối tượng thành viên sẽ được xây dựng đầy đủ bởi hàm tạo mặc định của nó và điều này có thể, ví dụ, phân bổ một số lượng bộ nhớ mặc định hoặc mở một số tệp mặc định. Tất cả công việc này có thể là vô ích nếu biểu thức bất kỳ và/hoặc toán tử gán làm cho đối tượng đóng tệp đó và/hoặc giải phóng bộ nhớ đó (e. g. , nếu hàm tạo mặc định không phân bổ vùng bộ nhớ đủ lớn hoặc nếu nó mở sai tệp)

Phần kết luận. Tất cả những thứ khác đều như nhau, mã của bạn sẽ chạy nhanh hơn nếu bạn sử dụng danh sách khởi tạo thay vì gán

Ghi chú. Không có sự khác biệt về hiệu suất nếu loại

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
006 là một số loại tích hợp/nội tại, chẳng hạn như
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
016 hoặc
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
017 hoặc
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
018. Nhưng ngay cả trong những trường hợp này, sở thích cá nhân của tôi là đặt các thành viên dữ liệu đó trong danh sách khởi tạo thay vì thông qua chuyển nhượng để thống nhất. Một đối số đối xứng khác có lợi cho việc sử dụng danh sách khởi tạo ngay cả đối với các loại tích hợp/nội tại.
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
019 không tĩnh và các thành viên dữ liệu tham chiếu không tĩnh không thể được gán một giá trị trong hàm tạo, vì vậy để đối xứng, việc khởi tạo mọi thứ trong danh sách khởi tạo là điều hợp lý

Bây giờ cho các trường hợp ngoại lệ. Mọi quy tắc đều có ngoại lệ (hmmm; có phải “mọi quy tắc đều có ngoại lệ” có ngoại lệ không? Làm tôi nhớ đến Định lý Bất toàn của Gödel), và có một vài ngoại lệ đối với quy tắc “sử dụng danh sách khởi tạo”. Điểm mấu chốt là sử dụng lẽ thường. nếu nó rẻ hơn, tốt hơn, nhanh hơn, v.v. không sử dụng chúng, thì bằng mọi cách, đừng sử dụng chúng. Điều này có thể xảy ra khi lớp của bạn có hai hàm tạo cần khởi tạo các thành viên dữ liệu của đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 theo các thứ tự khác nhau. Hoặc nó có thể xảy ra khi hai thành viên dữ liệu tự tham chiếu. Hoặc khi một thành viên dữ liệu cần tham chiếu đến đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 và bạn muốn tránh cảnh báo của trình biên dịch về việc sử dụng từ khóa
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 trước
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
023 bắt đầu phần thân của hàm tạo (khi trình biên dịch cụ thể của bạn tình cờ đưa ra cảnh báo cụ thể đó). Hoặc khi bạn cần làm (tham số, toàn cầu, v.v. ) trước khi sử dụng biến đó để khởi tạo một trong các thành viên
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 của bạn. Danh sách này không đầy đủ; . Vấn đề chỉ đơn giản là thế này. sử dụng suy nghĩ thông thường

Trình khởi tạo nên được sắp xếp như thế nào trong danh sách khởi tạo của hàm tạo?

Các lớp cơ sở ngay lập tức (từ trái sang phải), sau đó là các đối tượng thành viên (từ trên xuống dưới)

Nói cách khác, thứ tự của danh sách khởi tạo phải bắt chước thứ tự khởi tạo sẽ diễn ra. Hướng dẫn này không khuyến khích một loại lỗi phụ thuộc đơn hàng đặc biệt tinh vi bằng cách đưa ra manh mối rõ ràng, trực quan. Ví dụ: phần sau chứa một lỗi ghê tởm

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
02

Đầu ra của chương trình này sau

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
03

Lưu ý rằng __________ 1027 được sử dụng (________ 1028) trước khi nó được khởi tạo (________ 1029). Thay vào đó, nếu lập trình viên đã đọc và tuân theo hướng dẫn trong Câu hỏi thường gặp này, thì lỗi sẽ rõ ràng hơn. danh sách khởi tạo của

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
030 sẽ đọc là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
031, cho biết trực quan rằng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
027 đã được sử dụng trước khi được khởi tạo

Không phải tất cả các trình biên dịch đều đưa ra thông báo chẩn đoán cho những trường hợp này. Bạn đã được cảnh báo

Việc một đối tượng thành viên được khởi tạo bằng cách sử dụng một đối tượng thành viên khác trong biểu thức khởi tạo có hợp lý không?

Có, nhưng hãy cẩn thận và chỉ làm điều đó khi nó tăng thêm giá trị

Trong danh sách khởi tạo của hàm tạo, cách dễ nhất và an toàn nhất là tránh sử dụng một đối tượng thành viên từ đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 trong biểu thức khởi tạo của trình khởi tạo tiếp theo cho đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9. Hướng dẫn này ngăn cản

Do hướng dẫn này, hàm tạo sau sử dụng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
035 thay vì
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
036, mặc dù chúng tương đương nhau. Tiền tố
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
037 tránh sự phụ thuộc thứ tự không cần thiết và có thể tránh được

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
04

Một sự phụ thuộc thứ tự không cần thiết vào cách bố trí lớp của

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
038 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
039 sẽ được đưa ra nếu việc khởi tạo của hàm tạo
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
039 đã sử dụng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
036 thay vì
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
035. Tuy nhiên, sử dụng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
038 trong phần thân hàm tạo (
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
044) thì không sao. Không có phụ thuộc thứ tự nào được đưa ra vì toàn bộ danh sách khởi tạo được đảm bảo kết thúc trước khi phần thân của hàm tạo bắt đầu thực thi

Nếu một đối tượng thành viên phải được khởi tạo bằng một đối tượng thành viên khác thì sao?

Nhận xét tuyên bố của các thành viên dữ liệu có hiệu lực với

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
045

Nếu một hàm tạo khởi tạo một đối tượng thành viên của đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 bằng cách sử dụng một đối tượng thành viên khác của đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9,. Ràng buộc bảo trì quan trọng này nên được ghi lại trong phần thân của lớp

Ví dụ: trong hàm tạo bên dưới, trình khởi tạo cho

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
039 sử dụng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
038 để tránh lệnh gọi thừa tới
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
050, dẫn đến phụ thuộc thứ tự trong nội dung lớp

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
05

Lưu ý rằng nhận xét

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
045 được liệt kê với các thành viên dữ liệu bị ảnh hưởng trong nội dung lớp, không phải với danh sách khởi tạo hàm tạo nơi phụ thuộc thứ tự thực sự được tạo. Đó là bởi vì thứ tự của các đối tượng thành viên trong nội dung lớp là rất quan trọng;

Bạn có nên sử dụng con trỏ class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 9 trong hàm tạo không?

Một số người cảm thấy bạn không nên sử dụng con trỏ

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 trong hàm tạo vì đối tượng chưa được hình thành đầy đủ. Tuy nhiên, bạn có thể sử dụng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 trong hàm tạo (trong
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
023body
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
056 và thậm chí trong ) nếu bạn cẩn thận

Đây là một cái gì đó luôn hoạt động. ____1023body

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
056 của một hàm tạo (hoặc một hàm được gọi từ hàm tạo) có thể truy cập đáng tin cậy vào các thành viên dữ liệu được khai báo trong một lớp cơ sở và/hoặc các thành viên dữ liệu được khai báo trong lớp riêng của hàm tạo. Điều này là do tất cả các thành viên dữ liệu đó được đảm bảo đã được xây dựng đầy đủ vào thời điểm ____1023body
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
056 của hàm khởi tạo bắt đầu thực thi

Đây là một cái gì đó không bao giờ hoạt động.

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
023body
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
056 của hàm tạo (hoặc hàm được gọi từ hàm tạo) không thể xuống lớp dẫn xuất bằng cách gọi hàm thành viên
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
063 bị ghi đè trong lớp dẫn xuất. Nếu mục tiêu của bạn là truy cập hàm bị ghi đè trong lớp dẫn xuất,. Lưu ý rằng bạn sẽ không nhận được ghi đè trong lớp dẫn xuất độc lập với cách bạn gọi hàm thành viên
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
063. rõ ràng bằng cách sử dụng con trỏ
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 (e. g. ,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
066), ngầm sử dụng con trỏ
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 (e. g. ,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
068) hoặc thậm chí gọi một số hàm khác gọi hàm thành viên
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
063 trên đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 của bạn. Phía dưới là dòng này. ngay cả khi người gọi đang xây dựng một đối tượng của lớp dẫn xuất, trong quá trình khởi tạo của lớp cơ sở,. Bạn đã được cảnh báo

Đây là một cái gì đó đôi khi hoạt động. nếu bạn chuyển bất kỳ thành viên dữ liệu nào trong đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 sang thành viên dữ liệu khác, bạn phải đảm bảo rằng thành viên dữ liệu khác đã được khởi tạo. Tin vui là bạn có thể xác định xem thành viên dữ liệu khác đã (hoặc chưa) được khởi tạo hay chưa bằng cách sử dụng một số quy tắc ngôn ngữ đơn giản độc lập với trình biên dịch cụ thể mà bạn đang sử dụng. Tin xấu là bạn phải biết những quy tắc ngôn ngữ đó (e. g. , các đối tượng con của lớp cơ sở được khởi tạo trước (tra cứu thứ tự nếu bạn có nhiều và/hoặc kế thừa
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
063. ), thì các thành viên dữ liệu được định nghĩa trong lớp được khởi tạo theo thứ tự xuất hiện trong khai báo lớp). Nếu bạn không biết các quy tắc này, thì đừng chuyển bất kỳ thành viên dữ liệu nào từ đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 (bất kể bạn có sử dụng từ khóa
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 một cách rõ ràng hay không) cho bất kỳ thành viên dữ liệu nào khác. Và nếu bạn biết các quy tắc, hãy cẩn thận

"Thành ngữ xây dựng được đặt tên" là gì?

Một kỹ thuật cung cấp các thao tác xây dựng trực quan hơn và/hoặc an toàn hơn cho người dùng thuộc lớp của bạn

Vấn đề là các hàm tạo luôn có cùng tên với lớp. Do đó, cách duy nhất để phân biệt giữa các hàm tạo khác nhau của một lớp là theo danh sách tham số. Nhưng nếu có nhiều hàm tạo, sự khác biệt giữa chúng trở nên hơi nhỏ và dễ bị lỗi

Với Thành ngữ Constructor được đặt tên, bạn khai báo tất cả các hàm tạo của lớp trong phần

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
075 hoặc
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
076 và bạn cung cấp các phương thức
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
077
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 trả về một đối tượng. Các phương thức
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 này được gọi là “Các nhà xây dựng được đặt tên. ” Nói chung, có một phương pháp
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 như vậy cho mỗi cách khác nhau để xây dựng một đối tượng

Ví dụ: giả sử chúng ta đang xây dựng một lớp

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
081 đại diện cho một vị trí trên mặt phẳng X-Y. Hóa ra có hai cách phổ biến để chỉ định tọa độ 2 không gian. tọa độ chữ nhật (X+Y), tọa độ cực (Bán kính+Góc). (Đừng lo lắng nếu bạn không thể nhớ những điều này; vấn đề không phải là chi tiết cụ thể của các hệ tọa độ; vấn đề là có một số cách để tạo một đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
081. ) Thật không may, các tham số cho hai hệ tọa độ này giống nhau. hai
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
018. Điều này sẽ tạo ra một lỗi mơ hồ trong các hàm tạo quá tải

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
06

Một cách để giải quyết sự mơ hồ này là sử dụng Named Constructor Idiom

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
07

Giờ đây, người dùng của

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
081 có một cú pháp rõ ràng và rõ ràng để tạo các
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
081 trong cả hai hệ tọa độ

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
08

Đảm bảo rằng các hàm tạo của bạn nằm trong phần

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
076 nếu bạn muốn
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
081 có các lớp dẫn xuất

Thành ngữ Constructor được đặt tên cũng có thể được sử dụng để

Lưu ý rằng Thành ngữ Trình xây dựng được đặt tên, ít nhất là như đã triển khai ở trên, cũng nhanh như việc gọi trực tiếp một hàm tạo —

Trả về theo giá trị có nghĩa là có thêm bản sao và thêm chi phí không?

Không cần thiết

Tất cả (?) trình biên dịch cấp thương mại tối ưu hóa bản sao bổ sung, ít nhất là trong các trường hợp như minh họa trong

Để giữ cho ví dụ rõ ràng, hãy loại bỏ mọi thứ xuống những điều cần thiết nhất. Giả sử hàm

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
089 gọi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
090 (“rbv” là viết tắt của “return by value”) trả về một đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 theo giá trị

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
09

Bây giờ câu hỏi là, Sẽ có bao nhiêu đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091?

Điểm chính của Câu hỏi thường gặp này là câu trả lời là Không, trình biên dịch C++ cấp thương mại thực hiện trả về theo giá trị theo cách cho phép chúng loại bỏ chi phí hoạt động, ít nhất là trong các trường hợp đơn giản như những trường hợp được trình bày trong Câu hỏi thường gặp trước đó. Đặc biệt, tất cả (?) trình biên dịch C++ cấp thương mại sẽ tối ưu hóa trường hợp này

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
20

Chắc chắn trình biên dịch được phép tạo một đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 cục bộ, tạm thời, sau đó sao chép-xây dựng đối tượng tạm thời đó vào biến
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 trong
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
089, sau đó hủy đối tượng tạm thời đó. Nhưng tất cả (?) trình biên dịch C++ cấp thương mại sẽ không làm điều đó. câu lệnh
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
099 sẽ trực tiếp xây dựng chính
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5. Không phải là bản sao của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5, không phải là con trỏ tới
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5, không phải là tham chiếu tới
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5, mà chính là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5

Bạn có thể dừng ở đây nếu bạn không thực sự muốn hiểu đoạn trước, nhưng nếu bạn muốn biết điều bí mật (ví dụ, để bạn có thể dự đoán một cách đáng tin cậy khi trình biên dịch có thể và không thể cung cấp sự tối ưu hóa đó cho bạn), thì . Khi ________ 1089 gọi ________ 1090, trình biên dịch sẽ bí mật chuyển một con trỏ đến vị trí mà _________ 1090 được cho là sẽ xây dựng đối tượng “được trả lại”. Nó có thể trông giống như thế này (nó được hiển thị dưới dạng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
208 thay vì
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
209 vì đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 chưa được tạo)

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
21

Vì vậy, thành phần đầu tiên trong nước sốt bí mật là trình biên dịch (thường) chuyển đổi giá trị trả về thành chuyển qua con trỏ. Điều này có nghĩa là các trình biên dịch cấp thương mại không bận tâm đến việc tạo một. họ trực tiếp xây dựng đối tượng được trả về ở vị trí được chỉ ra bởi

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
211

Thành phần thứ hai trong nước sốt bí mật là các trình biên dịch thường triển khai các hàm tạo bằng cách sử dụng một kỹ thuật tương tự. Điều này phụ thuộc vào trình biên dịch và hơi lý tưởng hóa (tôi cố ý bỏ qua cách xử lý

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
088 và quá tải), nhưng trình biên dịch thường triển khai
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
213 bằng cách sử dụng một cái gì đó như thế này

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
22

Đặt những thứ này lại với nhau, trình biên dịch có thể triển khai câu lệnh

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
099 trong
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
090 bằng cách chuyển
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
211 làm con trỏ
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 của hàm tạo

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
23

Vì vậy,

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
089 chuyển
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
219 đến
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
090 và lần lượt
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
090 chuyển
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
219 đến hàm tạo (dưới dạng con trỏ
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9). Điều đó có nghĩa là nhà xây dựng trực tiếp xây dựng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5

Vào đầu những năm 90, tôi đã tổ chức một cuộc hội thảo cho nhóm trình biên dịch của IBM ở Toronto, và một trong số các kỹ sư của họ đã nói với tôi rằng họ nhận thấy việc tối ưu hóa giá trị trả về này nhanh đến mức bạn có thể nhận được nó ngay cả khi bạn không biên dịch với tính năng tối ưu hóa đã được bật. . Bởi vì tối ưu hóa trả về theo giá trị làm cho trình biên dịch tạo ra ít mã hơn, nên nó thực sự cải thiện thời gian biên dịch ngoài việc làm cho mã được tạo của bạn nhỏ hơn và nhanh hơn. Vấn đề là tối ưu hóa lợi nhuận theo giá trị hầu như được triển khai phổ biến, ít nhất là trong các trường hợp mã như trường hợp được hiển thị ở trên

suy nghĩ cuối cùng. cuộc thảo luận này chỉ giới hạn ở việc liệu có bất kỳ bản sao bổ sung nào của đối tượng được trả về trong lệnh gọi trả về theo giá trị hay không. Đừng nhầm lẫn điều đó với những thứ khác có thể xảy ra trong

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
089. Ví dụ: nếu bạn thay đổi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
089 từ
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
227 thành
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
228
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
229 (lưu ý
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
29 sau phần khai báo), trình biên dịch bắt buộc phải sử dụng toán tử gán của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 và trừ khi trình biên dịch có thể chứng minh rằng hàm tạo mặc định của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 theo sau là toán tử gán chính xác như . Tối ưu hóa trả về theo giá trị vẫn đóng vai trò của nó vì sẽ chỉ có một giá trị tạm thời, nhưng bằng cách thay đổi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
227 thành
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
228
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
229, bạn đã ngăn trình biên dịch loại bỏ giá trị tạm thời cuối cùng đó.

Còn việc trả về một biến cục bộ theo giá trị thì sao?

Khi mã của bạn trả về một biến cục bộ theo giá trị, trình biên dịch của bạn có thể tối ưu hóa hoàn toàn biến cục bộ - chi phí không gian bằng không và chi phí thời gian bằng không - biến cục bộ không bao giờ thực sự tồn tại như một đối tượng riêng biệt với biến mục tiêu của người gọi (xem bên dưới để biết chi tiết cụ thể . Các trình biên dịch khác không tối ưu hóa nó đi

Có vài(. ) của các trình biên dịch tối ưu hóa hoàn toàn biến cục bộ

  • GNU C++ (g++) kể từ ít nhất là phiên bản 3. 3. 3
  • (Những người khác cần được thêm vào; cần thêm thông tin)

Có vài(. ) của các trình biên dịch không tối ưu hóa biến cục bộ

  • Microsoft Visual C ++. MẠNG 2003
  • (Những người khác cần được thêm vào; cần thêm thông tin)

Đây là một ví dụ cho thấy những gì chúng tôi muốn nói trong Câu hỏi thường gặp này

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
24

Câu hỏi được giải quyết trong Câu hỏi thường gặp này là. Có bao nhiêu đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 thực sự được tạo trong hệ thống thời gian chạy? . tạm thời được tạo bởi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
239, biến
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 (trong
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
090) và biến
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 (trong
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
089). Tuy nhiên, như chúng ta đã thấy trước đó vào cùng một đối tượng, giảm tổng số đối tượng từ 3 xuống 2. Nhưng Câu hỏi thường gặp này đẩy nó thêm một bước nữa.
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 (trong
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
090) có hiển thị dưới dạng một đối tượng thời gian chạy riêng biệt từ
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 (trong
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
089) không?

Một số trình biên dịch, bao gồm nhưng không giới hạn ở những trình biên dịch được liệt kê ở trên, tối ưu hóa hoàn toàn biến cục bộ

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240. Trong các trình biên dịch đó, chỉ có một đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 trong đoạn mã trên. Biến
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
089 hoàn toàn giống hệt đối tượng với biến của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
090 là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240

Họ làm điều này theo cách tương tự như được mô tả. giá trị trả về trong hàm

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
090 được triển khai dưới dạng truyền qua con trỏ, trong đó con trỏ trỏ đến vị trí mà đối tượng được trả về sẽ được khởi tạo

Vì vậy, thay vì xây dựng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 như một đối tượng cục bộ, các trình biên dịch này chỉ cần xây dựng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
258 và mỗi khi chúng thấy biến
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 được sử dụng trong mã nguồn ban đầu, chúng sẽ thay thế bằng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
258. Sau đó, dòng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
261 trở thành đơn giản là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
262 vì đối tượng được trả về đã được tạo ở vị trí được chỉ định bởi người gọi

Đây là mã kết quả (giả)

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
25

báo trước. tối ưu hóa này chỉ có thể được áp dụng khi tất cả các câu lệnh

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
099 của hàm trả về cùng một biến cục bộ. Nếu một câu lệnh
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
099 trong
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
090 trả về biến cục bộ
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 nhưng một câu lệnh khác trả về một thứ khác, chẳng hạn như toàn cầu hoặc tạm thời, trình biên dịch không thể đặt bí danh biến cục bộ thành đích của người gọi,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5. Việc xác minh rằng tất cả các câu lệnh trả về của hàm trả về cùng một biến cục bộ đòi hỏi người viết trình biên dịch phải làm thêm công việc, đó thường là lý do tại sao một số trình biên dịch không thực hiện được việc tối ưu hóa trả về cục bộ theo giá trị đó

suy nghĩ cuối cùng. cuộc thảo luận này chỉ giới hạn ở việc liệu có bất kỳ bản sao bổ sung nào của đối tượng được trả về trong lệnh gọi trả về theo giá trị hay không. Đừng nhầm lẫn điều đó với những thứ khác có thể xảy ra trong

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
089. Ví dụ: nếu bạn thay đổi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
089 từ
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
227 thành
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
228
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
229 (lưu ý
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
29 sau phần khai báo), trình biên dịch bắt buộc phải sử dụng toán tử gán của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 và trừ khi trình biên dịch có thể chứng minh rằng hàm tạo mặc định của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 theo sau là toán tử gán chính xác như . Tối ưu hóa trả về theo giá trị vẫn đóng vai trò của nó vì sẽ chỉ có một giá trị tạm thời, nhưng bằng cách thay đổi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
227 thành
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
228
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
229, bạn đã ngăn trình biên dịch loại bỏ giá trị tạm thời cuối cùng đó.

Tại sao tôi không thể khởi tạo dữ liệu thành viên class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 078 của mình trong danh sách khởi tạo của nhà xây dựng?

Bởi vì bạn phải xác định rõ ràng các thành viên dữ liệu

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 của lớp mình

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
283

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
26

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
284 (hoặc
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
285 hoặc bất cứ thứ gì)

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
27

Ghi chú. trong một số trường hợp, định nghĩa của

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
286 có thể không chứa phần khởi tạo
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
287. Để biết chi tiết, xem và

Tại sao các lớp có thành viên dữ liệu class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 078 gặp lỗi liên kết?

Bởi vì. Nếu bạn không làm điều này, có thể bạn sẽ gặp lỗi trình liên kết

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
290. Ví dụ

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
28

Trình liên kết sẽ hét vào mặt bạn (

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
291) trừ khi bạn xác định (trái ngược với chỉ khai báo)
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
286 trong (chính xác) một trong các tệp nguồn của bạn

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
29

Vị trí thông thường để xác định thành viên dữ liệu

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
294
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 là tệp
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
284 (hoặc
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
285 hoặc bất kỳ phần mở rộng tệp nguồn nào bạn sử dụng)

Ghi chú. , tuy nhiên nếu bạn từng sử dụng thành viên dữ liệu, bạn vẫn cần xác định rõ ràng nó trong chính xác một đơn vị biên dịch. Trong trường hợp này, bạn không bao gồm trình khởi tạo

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
287 trong định nghĩa. bao gồm chủ đề này

Tôi có thể thêm bộ khởi tạo class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 287class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 29 vào phần khai báo của thành viên dữ liệu class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 078 class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 019 ở phạm vi lớp không?

Có, mặc dù với một số lưu ý quan trọng

Trước khi xem qua các cảnh báo, đây là một ví dụ đơn giản được phép

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
30

Và, , nó phải được định nghĩa chính xác trong một đơn vị biên dịch, mặc dù lần này không có phần khởi tạo

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
287

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
31

Thông báo trước là bạn chỉ có thể thực hiện việc này với các kiểu tích phân hoặc kiểu liệt kê và biểu thức khởi tạo phải là một biểu thức có thể được đánh giá tại thời điểm biên dịch. nó chỉ được chứa các hằng số khác, có thể được kết hợp với các toán tử tích hợp. Ví dụ:

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
308 là biểu thức hằng số thời gian biên dịch, cũng như
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
309 miễn là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
310 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
311 là hằng số thời gian biên dịch. Sau phần khai báo trên,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
312 cũng là một hằng số thời gian biên dịch. nó có thể được sử dụng trong các biểu thức hằng số thời gian biên dịch khác

Nếu bạn đã từng lấy địa chỉ của

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
312, chẳng hạn như chuyển nó theo tham chiếu hoặc nói rõ ràng là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
314, trình biên dịch sẽ đảm bảo rằng nó có một địa chỉ duy nhất. Nếu không,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
312 thậm chí sẽ không chiếm dung lượng trong vùng dữ liệu tĩnh của quy trình của bạn

"Lệnh khởi tạo ____1078 'thất bại' (sự cố)" là gì?

Một cách tinh vi để làm hỏng chương trình của bạn

Vấn đề thứ tự khởi tạo

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 là một khía cạnh rất tế nhị và thường bị hiểu sai của C++. Thật không may, nó rất khó phát hiện — lỗi thường xảy ra trước khi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
318 bắt đầu

Tóm lại, giả sử bạn có hai đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 tồn tại trong các tệp nguồn riêng biệt, chẳng hạn như
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
322 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
323. Giả sử thêm rằng việc khởi tạo đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 (thường là hàm tạo của đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240) gọi một số phương thức trên đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5

Đó là nó. Nó đơn giản mà

Phần khó khăn là bạn có 50% -50% cơ hội làm hỏng chương trình. Nếu đơn vị biên dịch cho

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
322 tình cờ được khởi tạo trước, thì tất cả đều ổn. Nhưng nếu đơn vị biên dịch cho
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
323 được khởi tạo trước, thì quá trình khởi tạo của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 sẽ được chạy trước quá trình khởi tạo của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 và bạn đã nâng cốc chúc mừng. e. g. , Hàm tạo của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 có thể gọi một phương thức trên đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5, nhưng đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 vẫn chưa được tạo

Để biết cách giải quyết vấn đề, hãy xem

Ghi chú. Vấn đề thứ tự khởi tạo tĩnh cũng có thể áp dụng cho các loại tích hợp/nội tại

Làm cách nào để ngăn chặn “sự cố thứ tự khởi tạo ____1078”?

Để ngăn , hãy sử dụng Thành ngữ Xây dựng khi sử dụng lần đầu, được mô tả bên dưới

Ý tưởng cơ bản của Thành ngữ Xây dựng khi sử dụng lần đầu là bọc đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 của bạn bên trong một hàm. Ví dụ: giả sử bạn có hai lớp,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
337. Có một đối tượng namespace-scope/global
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 được gọi là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5, và một namespace-scope/global
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
337 object được gọi là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240. Hàm tạo của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
337 gọi phương thức
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
343 trên đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5. Tệp
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
322 định nghĩa đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
32

Tệp

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
323 định nghĩa đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
33

Để hoàn thiện, hàm tạo

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
337 có thể trông giống như thế này

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
34

Bạn sẽ có một nếu

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 được xây dựng trước
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5. Như đã viết ở trên, thảm họa này sẽ xảy ra khoảng 50% thời gian, vì hai đối tượng được khai báo trong các tệp nguồn khác nhau và các tệp nguồn đó không đưa ra gợi ý nào cho trình biên dịch hoặc trình liên kết về thứ tự khởi tạo tĩnh

Có nhiều giải pháp cho vấn đề này, nhưng một giải pháp rất đơn giản và hoàn toàn di động là Thành ngữ Xây dựng khi sử dụng lần đầu. thay thế đối tượng namespace-scope/global

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 bằng namespace-scope/hàm toàn cầu
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
7 trả về đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 theo tham chiếu

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
35

Vì các đối tượng cục bộ

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 được xây dựng lần đầu tiên luồng điều khiển qua khai báo của chúng (chỉ), nên câu lệnh
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
358 ở trên sẽ chỉ xảy ra một lần. lần đầu tiên
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
7 được gọi. Mọi cuộc gọi tiếp theo sẽ trả về cùng một đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 (đối tượng được chỉ định bởi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
361). Sau đó, tất cả những gì bạn làm là thay đổi cách sử dụng của bạn từ
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 thành
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
7

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
36

Đây được gọi là Thành ngữ xây dựng khi sử dụng lần đầu bởi vì nó chỉ làm được điều đó. đối tượng (phạm vi không gian tên hợp lý / toàn cầu)

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 được xây dựng trong lần sử dụng đầu tiên

Nhược điểm của phương pháp này là đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 không bao giờ bị hủy. Nếu đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 có một hàm hủy với các tác dụng phụ quan trọng, thì đó là câu trả lời cho mối quan tâm này;

Ghi chú. Vấn đề thứ tự khởi tạo tĩnh cũng có thể áp dụng cho các loại tích hợp/nội tại

Tại sao Thành ngữ Xây dựng khi sử dụng lần đầu không sử dụng đối tượng class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 078 thay vì con trỏ class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 078?

Câu trả lời ngắn. có thể sử dụng một đối tượng tĩnh, nhưng làm như vậy sẽ mở ra một vấn đề khác (tinh vi không kém, khó chịu không kém)

Câu trả lời dài. đôi khi mọi người lo lắng về thực tế là “rò rỉ. ” Trong nhiều trường hợp, đây không phải là vấn đề, nhưng nó lại là vấn đề trong một số trường hợp. Ghi chú. mặc dù đối tượng được chỉ ra bởi

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
361 trong Câu hỏi thường gặp trước đó không bao giờ bị xóa, bộ nhớ không thực sự bị "rò rỉ" khi chương trình thoát do hệ điều hành tự động lấy lại tất cả bộ nhớ trong đống của chương trình khi chương trình đó thoát. Nói cách khác, lần duy nhất bạn cần lo lắng về điều này là khi hàm hủy của đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 thực hiện một số hành động quan trọng (chẳng hạn như ghi nội dung nào đó vào tệp) đôi khi phải xảy ra trong khi chương trình đang thoát

Trong những trường hợp mà đối tượng xây dựng trên lần sử dụng đầu tiên (trong trường hợp này là

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43) cuối cùng cần phải bị hủy, bạn có thể cân nhắc thay đổi chức năng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
7 như sau

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
37

Tuy nhiên, có (hay đúng hơn là có thể có) một vấn đề khá tế nhị với sự thay đổi này. Để hiểu vấn đề tiềm ẩn này, hãy nhớ tại sao chúng ta lại làm tất cả những điều này ngay từ đầu. chúng tôi cần đảm bảo 100% đối tượng tĩnh của chúng tôi (a) được xây dựng trước lần sử dụng đầu tiên và (b) không bị hủy cho đến sau lần sử dụng cuối cùng. Rõ ràng sẽ là một thảm họa nếu bất kỳ đối tượng tĩnh nào được sử dụng trước khi xây dựng hoặc sau khi phá hủy. Thông báo ở đây là bạn cần phải lo lắng về hai tình huống (khởi tạo tĩnh và khử khởi tạo tĩnh), không chỉ một

Bằng cách thay đổi khai báo từ

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
373 thành
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
374, chúng tôi vẫn xử lý chính xác tình huống khởi tạo nhưng chúng tôi không còn xử lý tình huống khử khởi tạo nữa. Ví dụ: nếu có 3 đối tượng tĩnh, chẳng hạn như
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
310,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
311 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
377, sử dụng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
361 trong quá trình hủy của chúng, thì cách duy nhất để tránh thảm họa khử khởi tạo tĩnh là nếu
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
361 bị hủy sau cả ba đối tượng

điểm là đơn giản. nếu có bất kỳ đối tượng tĩnh nào khác mà trình hủy của chúng có thể sử dụng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
361 sau khi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
361 bị hủy, bang, bạn chết. Nếu các hàm tạo của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
310,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
311 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
377 sử dụng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
361, thông thường bạn sẽ ổn vì hệ thống thời gian chạy sẽ, trong quá trình khử khởi tạo tĩnh, hủy
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
361 sau khi đối tượng cuối cùng trong số ba đối tượng đó bị hủy. Tuy nhiên, nếu
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
310 và/hoặc
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
311 và/hoặc
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
377 không sử dụng được
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
361 trong hàm tạo của chúng và/hoặc nếu bất kỳ mã nào ở bất kỳ đâu lấy được địa chỉ của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
361 và trao nó cho một số đối tượng tĩnh khác, thì mọi khả năng đặt cược đều bị tắt và bạn phải rất,

Có, nhưng nó có các chi phí không nhỏ khác

Kỹ thuật đảm bảo cả khởi tạo class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 078 và khử khởi tạo class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 078 là gì?

Câu trả lời ngắn. sử dụng Thành ngữ Bộ đếm tiện lợi (nhưng đảm bảo rằng bạn hiểu sự đánh đổi không tầm thường. )

Động lực

  • Việc sử dụng một con trỏ và cố ý làm rò rỉ đối tượng. Điều đó thường vô hại, vì hệ điều hành thường sẽ dọn sạch bộ nhớ của một tiến trình khi tiến trình kết thúc. Tuy nhiên, nếu đối tượng có một hàm hủy không tầm thường với các tác dụng phụ quan trọng, chẳng hạn như ghi vào tệp hoặc một số hành động không bay hơi khác, thì bạn cần thêm
  • Đó là nơi đến. nó không làm rò rỉ đối tượng, nhưng nó không kiểm soát thứ tự khử khởi tạo tĩnh, do đó (rất. ) không an toàn khi sử dụng đối tượng trong quá trình khử khởi tạo tĩnh, nghĩa là từ một hàm hủy của một đối tượng được khai báo tĩnh khác
  • Nếu bạn cần kiểm soát thứ tự của cả khởi tạo tĩnh và khử khởi tạo tĩnh, nghĩa là nếu bạn muốn truy cập một đối tượng được cấp phát tĩnh từ cả hàm tạo và hàm hủy của các đối tượng tĩnh khác, thì hãy tiếp tục đọc
  • Nếu không thì chạy đi

LÀM. VIẾT NÀY LÊN

LÀM. VIẾT LÊN CÁC GIAO DỊCH - bây giờ bạn đã biết cách sử dụng Thành ngữ Bộ đếm tiện lợi, hãy chắc chắn rằng bạn hiểu cả thời điểm và (đặc biệt là. ) khi không sử dụng. Một kích thước không phù hợp với tất cả

Làm cách nào để ngăn chặn “sự cố thứ tự khởi tạo class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 078” cho các thành viên dữ liệu class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 078 của tôi?

Sử dụng Thành ngữ cấu trúc khi sử dụng lần đầu, về cơ bản giống như , hoặc có lẽ , nhưng nó sử dụng hàm thành viên

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 thay vì hàm phạm vi không gian tên/hàm toàn cầu

Giả sử bạn có một lớp

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
397 có một đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
38

Đương nhiên, thành viên

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 này được khởi tạo riêng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
39

Đương nhiên, đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 cũng sẽ được sử dụng trong một hoặc nhiều phương thức của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
397

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
50

Nhưng bây giờ “kịch bản thảm họa” là nếu ai đó ở đâu đó gọi phương thức này trước khi đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
43 được xây dựng. Ví dụ: nếu người khác tạo một đối tượng tĩnh
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
397 và gọi phương thức
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
505 của nó trong quá trình khởi tạo
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078, thì bạn sẽ phụ thuộc vào trình biên dịch về việc liệu trình biên dịch sẽ xây dựng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
507 trước hay sau khi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
505 được gọi. (Lưu ý rằng ủy ban ANSI/ISO C++ đang giải quyết vấn đề này, nhưng trình biên dịch vẫn chưa có sẵn để xử lý những thay đổi này; hãy theo dõi không gian này để cập nhật trong tương lai. )

Trong mọi trường hợp, việc thay đổi thành viên dữ liệu

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
507
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 thành hàm thành viên
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 luôn có thể di động và an toàn

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
51

Đương nhiên, thành viên

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 này được khởi tạo riêng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
52

Sau đó, bạn chỉ cần thay đổi bất kỳ cách sử dụng nào của

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
006 thành
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
7

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
53

Nếu bạn siêu nhạy cảm với hiệu suất và bạn lo lắng về chi phí hoạt động của một lệnh gọi chức năng bổ sung trên mỗi lệnh gọi của

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
515, bạn có thể thiết lập một
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
517 để thay thế. Như bạn nhớ lại,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
078 cục bộ chỉ được khởi tạo một lần (lần đầu tiên kiểm soát chảy qua khai báo của chúng), vì vậy điều này sẽ chỉ gọi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
519 một lần. lần đầu tiên
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
515 được gọi

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
54

Ghi chú. Vấn đề thứ tự khởi tạo tĩnh cũng có thể áp dụng cho các loại tích hợp/nội tại

Tôi có cần lo lắng về “vấn đề thứ tự khởi tạo class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 078” đối với các biến thuộc loại tích hợp/nội tại không?

Đúng

Nếu bạn khởi tạo kiểu tích hợp/nội tại của mình bằng cách sử dụng lệnh gọi hàm, vấn đề thứ tự khởi tạo tĩnh có thể giết bạn cũng tệ như với các kiểu do người dùng định nghĩa/lớp. Ví dụ: đoạn mã sau hiển thị lỗi

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
55

Đầu ra của chương trình nhỏ này sẽ cho thấy rằng nó sử dụng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 trước khi khởi tạo nó. Giải pháp, như trước đây, là

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
56

Tất nhiên, bạn có thể đơn giản hóa việc này bằng cách di chuyển mã khởi tạo cho

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 vào các chức năng tương ứng của chúng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
57

Và, nếu bạn có thể loại bỏ các câu lệnh in, bạn có thể đơn giản hóa chúng thành một thứ thực sự đơn giản

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
58

Hơn nữa, vì

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
240 được khởi tạo bằng một biểu thức hằng, nên nó không còn cần hàm bao bọc nữa — nó có thể lại là một biến đơn giản

Làm cách nào tôi có thể xử lý một hàm tạo không thành công?

Ném một ngoại lệ. Để biết chi tiết, xem

"Thành ngữ tham số được đặt tên" là gì?

Đó là một cách khá hữu ích để khai thác

Vấn đề cơ bản được giải quyết bởi Thành ngữ tham số được đặt tên là C++ chỉ hỗ trợ các tham số vị trí. Ví dụ: người gọi hàm không được phép nói, “Đây là giá trị cho tham số chính thức

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
526 và thứ khác này là giá trị cho tham số chính thức
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
527. ” Tất cả những gì bạn có thể làm trong C++ (và C và Java) là nói, “Đây là tham số đầu tiên, đây là tham số thứ hai, v.v. ” Phương án thay thế, được gọi là các tham số được đặt tên và được triển khai bằng ngôn ngữ Ada, đặc biệt hữu ích nếu một hàm nhận một số lượng lớn các tham số hầu hết có thể mặc định

Trong nhiều năm, mọi người đã đưa ra rất nhiều cách giải quyết cho việc thiếu các tham số được đặt tên trong C và C++. Một trong số đó liên quan đến việc chôn các giá trị tham số trong một tham số chuỗi sau đó phân tích cú pháp chuỗi này trong thời gian chạy. Đây là những gì được thực hiện trong tham số thứ hai của

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
528, ví dụ. Một cách giải quyết khác là kết hợp tất cả các tham số boolean trong một bản đồ bit, sau đó hàm gọi hoặc một loạt các hằng số được dịch chuyển bit lại với nhau để tạo ra tham số thực tế. Đây là những gì được thực hiện trong tham số thứ hai của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
529, ví dụ. Những cách tiếp cận này hoạt động, nhưng kỹ thuật sau đây tạo ra mã người gọi rõ ràng hơn, dễ viết hơn, dễ đọc hơn và nói chung là thanh lịch hơn

Ý tưởng, được gọi là Thành ngữ tham số được đặt tên, là thay đổi các tham số của hàm thành các phương thức của một lớp mới được tạo, trong đó tất cả các phương thức này trả về

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
530 theo tham chiếu. Sau đó, bạn chỉ cần đổi tên hàm chính thành một phương thức “do-it” không tham số trên lớp đó

Chúng tôi sẽ làm một ví dụ để làm cho đoạn trước dễ hiểu hơn

Ví dụ sẽ dành cho khái niệm “mở tệp”. Giả sử khái niệm đó yêu cầu một cách hợp lý một tham số cho tên của tệp và tùy ý cho phép các tham số để biết liệu tệp có nên được mở ở chế độ chỉ đọc hay không. đọc ghi vs. chỉ ghi, tệp có nên được tạo hay không nếu nó chưa tồn tại, vị trí ghi nên ở cuối (“chắp thêm”) hay ở đầu (“ghi đè”), kích thước khối nếu tệp . quyền truy cập độc quyền và có thể là một số quyền truy cập khác. Nếu chúng tôi triển khai khái niệm này bằng cách sử dụng một hàm bình thường với các tham số vị trí, mã người gọi sẽ rất khó đọc. sẽ có tới 8 tham số vị trí và người gọi có thể sẽ mắc nhiều lỗi. Vì vậy, thay vào đó chúng tôi sử dụng Thành ngữ tham số được đặt tên

Trước khi chúng tôi thực hiện triển khai, đây là mã trình gọi có thể trông như thế nào, giả sử bạn sẵn sàng chấp nhận tất cả các tham số mặc định của hàm

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
59

Đó là trường hợp dễ dàng. Bây giờ, đây là giao diện nếu bạn muốn thay đổi một loạt các tham số

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
00

Lưu ý cách các “tham số”, nếu gọi chúng như vậy là hợp lý, sắp xếp theo thứ tự ngẫu nhiên (chúng không theo vị trí) và tất cả chúng đều có tên. Vì vậy, lập trình viên không cần phải nhớ thứ tự của các tham số và tên (hy vọng) rõ ràng

Vì vậy, đây là cách thực hiện nó. trước tiên, chúng tôi tạo một lớp (

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
531) chứa tất cả các giá trị tham số dưới dạng thành viên dữ liệu
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
075. Các tham số bắt buộc (trong trường hợp này, tham số bắt buộc duy nhất là tên tệp) được triển khai như một tham số vị trí bình thường trên hàm tạo của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
531, nhưng hàm tạo đó không thực sự mở tệp. Sau đó, tất cả các tham số tùy chọn (chỉ đọc so với. đọc ghi, v.v. ) trở thành các phương thức. Các phương pháp này (e. g. ,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
534,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
535, v.v. ) trả về một tham chiếu đến đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
9 của chúng để các lệnh gọi phương thức có thể được

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
01

Điều khác duy nhất cần làm là tạo hàm tạo cho lớp

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
537 để lấy một đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
531

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
02

Hàm tạo này nhận các tham số thực tế từ đối tượng OpenFile, sau đó thực sự mở tệp

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
03

Lưu ý rằng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
531 tuyên bố
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
537 là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
541 của nó, theo cách đó

Vì mỗi hàm thành viên trong chuỗi trả về một tham chiếu nên không có sự sao chép đối tượng và chuỗi có hiệu quả cao. Hơn nữa, nếu các hàm thành viên khác nhau là

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
544, thì mã đối tượng được tạo có thể sẽ ngang bằng với mã kiểu C đặt các thành viên khác nhau của một
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
545. Tất nhiên, nếu các chức năng thành viên không phải là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
544, thì có thể có một chút tăng kích thước mã và giảm một chút hiệu suất (nhưng chỉ khi quá trình xây dựng xảy ra trên đường dẫn quan trọng của chương trình gắn với CPU; đây là một hộp sâu I

Tại sao tôi gặp lỗi sau khi khai báo đối tượng class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 091 qua class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 548?

Bởi vì điều đó không tạo ra một đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 - nó khai báo một hàm không phải thành viên trả về một đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091. Thuật ngữ “Most Vexing Parse” được đặt ra bởi Scott Myers để mô tả tình huống này

Điều này thực sự sẽ gây tổn thương;

Đầu tiên, đây là một lời giải thích tốt hơn về vấn đề. Giả sử có một lớp tên là

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
551 có một ctor mặc định. Đây thậm chí có thể là một lớp thư viện chẳng hạn như
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
552, nhưng bây giờ chúng ta sẽ chỉ gọi nó là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
551

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
04

Bây giờ, giả sử có một lớp khác tên là

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 có một diễn viên lấy một
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
551. Như trước đây, điều này có thể được xác định bởi một người khác ngoài bạn

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
05

Bây giờ bạn muốn tạo một đối tượng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 bằng cách sử dụng một
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
551 tạm thời. Nói cách khác, bạn muốn tạo một đối tượng thông qua
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
558 và chuyển đối tượng đó tới ctor
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 để tạo một đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 cục bộ có tên là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
06

Đó là một câu chuyện dài, nhưng có một giải pháp (hy vọng bạn đang ngồi xuống. ) là thêm một cặp

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
562 xung quanh phần
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
558

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
07

Một giải pháp khác là sử dụng

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
287 trong phần khai báo của bạn (xem bản in đẹp bên dưới)

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
08

Ghi chú. Giải pháp trên yêu cầu

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
565 để có thể truy cập hàm tạo bản sao
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091. Trong hầu hết các tình huống, điều đó có nghĩa là trình tạo bản sao của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 cần phải là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
077, mặc dù nó không cần phải là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
077 trong trường hợp ít phổ biến hơn khi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
565 là bạn của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
571. Nếu bạn không chắc điều đó có nghĩa là gì, hãy thử. nếu mã của bạn biên dịch, bạn đã vượt qua bài kiểm tra

Đây là một giải pháp khác (bản in đẹp hơn bên dưới)

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
09

Ghi chú. Từ "thường" ở trên có nghĩa là điều này. điều trên chỉ thất bại khi

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
572 hoặc khi không thể truy cập trình tạo bản sao của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 (thường khi đó là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
075 hoặc
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
076 và mã của bạn không phải là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
541). Nếu bạn không chắc điều đó có nghĩa là gì, hãy dành 60 giây và biên dịch nó. Bạn được đảm bảo tìm hiểu xem nó có hoạt động hay không trong thời gian biên dịch, vì vậy nếu nó được biên dịch rõ ràng, nó sẽ hoạt động trong thời gian chạy

Tuy nhiên, giải pháp tốt nhất, việc tạo ra giải pháp này ít nhất được thúc đẩy một phần bởi thực tế là Câu hỏi thường gặp này tồn tại, là sử dụng , thay thế

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
562 xung quanh cuộc gọi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
558 bằng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
580 thay thế

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
10

Đó là kết thúc của các giải pháp; . Khi trình biên dịch nhìn thấy

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
548, nó nghĩ rằng phần
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
558 đang khai báo một hàm không phải thành viên trả về một đối tượng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
551, vì vậy nó nghĩ rằng bạn đang khai báo sự tồn tại của một hàm gọi là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
5 trả về một
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 và nhận một tham số duy nhất của . ”

Bây giờ đây là phần buồn. Trên thực tế, nó thật thảm hại. Một số máy bay không người lái không suy nghĩ ngoài kia sẽ bỏ qua đoạn cuối cùng đó, sau đó họ sẽ áp đặt một tiêu chuẩn viết mã ngu ngốc, không chính xác, không liên quan và đơn giản có nội dung như: “Không bao giờ tạo tạm thời bằng cách sử dụng hàm tạo mặc định” hoặc “Luôn luôn . Nếu đó là bạn, hãy tự bắn mình trước khi gây thêm sát thương. Những người không hiểu vấn đề không nên nói cho người khác cách giải quyết. harp

(Đó chủ yếu là lưỡi trong má. Nhưng có một hạt sự thật trong đó. Vấn đề thực sự là mọi người có xu hướng tôn thờ sự nhất quán và họ có xu hướng ngoại suy từ những điều tối nghĩa đến những điều phổ biến. Điều đó không khôn ngoan. )

Mục đích của từ khóa class Fred { public: Fred(); // ... }; int main() { Fred a[10]; // Calls the default constructor 10 times Fred* p = new Fred[10]; // Calls the default constructor 10 times // ... } 573 là gì?

Từ khóa

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
573 là một trang trí tùy chọn cho các hàm tạo và toán tử chuyển đổi để báo cho trình biên dịch biết rằng một hàm tạo hoặc toán tử chuyển đổi nhất định có thể không được sử dụng để chuyển một biểu thức sang loại lớp của nó

Ví dụ: không có từ khóa

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
573, đoạn mã sau là hợp lệ

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
11

Nhưng đôi khi bạn muốn ngăn chặn loại quảng cáo ngầm hoặc chuyển đổi loại ngầm này. Ví dụ: nếu

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 thực sự là một vùng chứa dạng mảng và 42 là kích thước ban đầu, bạn có thể muốn cho phép người dùng của mình nói,
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
592 hoặc có thể là
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
593, nhưng không chỉ
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
594. Nếu đúng như vậy, bạn nên sử dụng từ khóa
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
573

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
12

Bạn có thể kết hợp các hàm tạo

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
573 và không phải ____2573 và các toán tử chuyển đổi trong cùng một lớp. Ví dụ: lớp này có một hàm tạo
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
573 lấy một
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
599 nhưng một hàm tạo không phải
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
573 lấy một
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
001 và có thể được chuyển đổi hoàn toàn thành gấp đôi, nhưng chỉ được chuyển đổi rõ ràng thành bool

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
13

Đoạn mã trên sẽ in như sau

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
14

Biến

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
310 được khởi tạo bằng cách sử dụng hàm tạo
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
003 vì không thể sử dụng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
004 trong cách truyền ngầm định, nhưng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
005 có thể được hiểu là một
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
006, nghĩa là, như
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
007 và được chuyển đổi ngầm định sang
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
091 bằng cách sử dụng
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
009. Đây có thể hoặc không phải là những gì bạn dự định, nhưng đây là những gì xảy ra

Tại sao hàm tạo của tôi không hoạt động bình thường?

Đây là một câu hỏi có nhiều dạng. Như là

  • Tại sao trình biên dịch sao chép các đối tượng của tôi khi tôi không muốn?
  • Làm cách nào để tắt sao chép?
  • Làm cách nào để ngừng chuyển đổi ngầm định?
  • Làm thế nào mà int của tôi biến thành một số phức?

Theo mặc định, một lớp được cung cấp một hàm tạo sao chép và một phép gán sao chép sao chép tất cả các phần tử, một hàm tạo di chuyển và một phép gán di chuyển sẽ di chuyển tất cả các phần tử. Ví dụ

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
15

Ở đây chúng tôi nhận được

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
010 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
011. Đó thường chính xác là những gì bạn muốn (và cần thiết cho khả năng tương thích với C), nhưng hãy cân nhắc

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
16

Ở đây, bản sao mặc định cung cấp cho chúng tôi

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
012 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
013. Điều này dẫn đến thảm họa. khi chúng tôi thoát khỏi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
3, hàm hủy cho
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
015 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
016 được gọi và đối tượng được trỏ bởi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
017 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
018 bị xóa hai lần

Làm thế nào để chúng ta tránh điều này?

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
17

Nếu chúng ta cần sao chép hoặc di chuyển, tất nhiên chúng ta có thể xác định các bộ khởi tạo và phép gán thích hợp để cung cấp ngữ nghĩa mong muốn

Bây giờ quay lại

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
081. Đối với
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
081, ngữ nghĩa sao chép mặc định vẫn ổn, vấn đề là hàm tạo

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
18

Mọi người cung cấp các đối số mặc định để có được sự tiện lợi được sử dụng cho

class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
021 và
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
022. Sau đó, một số ngạc nhiên bởi việc chuyển đổi
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
023 thành
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
024 trong cuộc gọi của
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
3. Hàm tạo này xác định một chuyển đổi. Theo mặc định, đó là một chuyển đổi ngầm. Để yêu cầu chuyển đổi rõ ràng như vậy, hãy khai báo hàm tạo
class Fred {
public:
  Fred();
  // ...
};

int main()
{
  Fred a[10];              // Calls the default constructor 10 times
  Fred* p = new Fred[10];  // Calls the default constructor 10 times
  // ...
}
573

Chúng ta có thể chuyển con trỏ tới hàm tạo không?

Một số người cho rằng bạn không nên sử dụng con trỏ this trong hàm tạo vì đối tượng chưa được hình thành đầy đủ. Tuy nhiên bạn có thể sử dụng điều này trong hàm tạo (trong {body} và thậm chí trong danh sách khởi tạo) nếu bạn cẩn thận .

Hàm tạo có nên có tham số không?

Trình tạo cũng có thể nhận tham số , được sử dụng để khởi tạo thuộc tính.

Con trỏ có thể được truyền theo giá trị không?

Con trỏ được truyền theo giá trị như mọi thứ khác . Điều đó có nghĩa là nội dung của biến con trỏ (địa chỉ của đối tượng được trỏ tới) được sao chép. Điều đó có nghĩa là nếu bạn thay đổi giá trị của con trỏ trong thân hàm, sự thay đổi đó sẽ không được phản ánh trong con trỏ bên ngoài mà vẫn trỏ đến đối tượng cũ.

Hàm tạo mặc định có thể có tham số không?

Hàm tạo mặc định là hàm tạo không có tham số hoặc nếu có tham số thì tất cả các tham số đều có giá trị mặc định . Nếu không có hàm tạo do người dùng định nghĩa nào tồn tại cho một lớp A và cần một hàm tạo, thì trình biên dịch sẽ ngầm khai báo một hàm tạo không tham số mặc định A. MỘT().