2011-01-03 15 views

Trả lời

56

Giả sử rằng những giá trị này là loại nguyên thủy, sau đó không, không có sự khác biệt. Danh sách khởi tạo chỉ tạo sự khác biệt khi bạn có đối tượng là thành viên, vì thay vì sử dụng khởi tạo mặc định theo sau gán, danh sách khởi tạo cho phép bạn khởi tạo đối tượng thành giá trị cuối cùng của nó. Điều này thực sự có thể nhanh hơn đáng kể.

+16

như Richard cho biết, nó tạo sự khác biệt nếu các giá trị đó là các loại nguyên thủy và const, danh sách khởi tạo là cách duy nhất để gán giá trị cho các thành viên const. – thbusch

+11

Nó chỉ hoạt động như được mô tả khi các biến không phải là tham chiếu hoặc hằng số, nếu chúng là hằng số hoặc tham chiếu, nó sẽ không biên dịch mà không sử dụng danh sách khởi tạo. – stefanB

17

Có. Trong trường hợp đầu tiên mà bạn có thể khai báo _capacity, _data_len như hằng số:

class MyClass 
{ 
private: 
    const int _capacity; 
    const void *_data; 
    const int _len; 
// ... 
}; 

Đây sẽ là quan trọng nếu bạn muốn đảm bảo const -ness của các biến Ví dụ khi tính toán giá trị của họ trong thời gian chạy, ví dụ:

MyClass::MyClass() : 
    _capacity(someMethod()), 
    _data(someOtherMethod()), 
    _len(yetAnotherMethod()) 
{ 
} 

const trường phải được khởi tạo trong danh sách initializer hoặc các loại cơ bản phải cung cấp khuyết điểm parameterless công cộng tructors (loại nguyên thủy nào).

+2

Điều tương tự cũng xảy ra. Nếu lớp của bạn có các thành viên tham chiếu, chúng * phải * được khởi tạo trong danh sách khởi tạo. –

+0

@Mark: +1. Cảm ơn! –

3

Sự khác biệt lớn là nhiệm vụ có thể khởi tạo thành viên của một lớp cha; bộ khởi tạo chỉ hoạt động trên các thành viên được khai báo ở phạm vi lớp hiện tại.

2

Phụ thuộc vào các loại có liên quan. Sự khác biệt là tương tự giữa

std::string a; 
a = "hai"; 

std::string a("hai"); 

nơi hình thức thứ hai là khởi list- có nghĩa là, nó làm cho một sự khác biệt nếu loại đòi hỏi đối số nhà xây dựng hoặc là hiệu quả hơn với các đối số nhà xây dựng.

5

Tôi sẽ thêm rằng nếu bạn có các thành viên của loại lớp không có hàm tạo mặc định, khởi tạo là cách duy nhất để xây dựng lớp học của bạn.

77

Bạn cần phải sử dụng danh sách khởi để khởi tạo các thành viên liên tục, tài liệu tham khảo và lớp cơ sở

Khi bạn cần phải khởi tạo thành viên liên tục, tài liệu tham khảo và truyền tham số cho constructor của lớp cha, như đã đề cập trong ý kiến, bạn cần phải sử dụng danh sách khởi tạo.

struct aa 
{ 
    int i; 
    const int ci;  // constant member 

    aa() : i(0) {} // will fail, constant member not initialized 
}; 

struct aa 
{ 
    int i; 
    const int ci; 

    aa() : i(0) { ci = 3;} // will fail, ci is constant 
}; 

struct aa 
{ 
    int i; 
    const int ci; 

    aa() : i(0), ci(3) {} // works 
}; 

Ví dụ (không đầy đủ) lớp/struct chứa tài liệu tham khảo:

struct bb {}; 

struct aa 
{ 
    bb& rb; 
    aa(bb& b) : rb(b) {} 
}; 

// usage: 

bb b; 
aa a(b); 

Và ví dụ về khởi tạo lớp cơ sở mà đòi hỏi một tham số (ví dụkhông có hàm tạo mặc định nào):

struct bb {}; 

struct dd 
{ 
    char c; 
    dd(char x) : c(x) {} 
}; 

struct aa : dd 
{ 
    bb& rb; 
    aa(bb& b) : dd('a'), rb(b) {} 
}; 
+4

Và nếu '_capacity',' _data' và '_len' có các kiểu lớp không có các hàm tạo mặc định có thể truy cập? –

+2

Bạn gọi những gì các nhà xây dựng có sẵn, nếu bạn cần phải thiết lập các giá trị hơn sau đó bạn gọi chúng từ cơ thể của nhà xây dựng của bạn. Sự khác biệt ở đây là bạn không thể khởi tạo thành viên 'const' trong phần thân của hàm tạo, bạn phải sử dụng danh sách khởi tạo - các thành viên không' const' có thể được khởi tạo trong danh sách khởi tạo hoặc trong phần thân của hàm tạo. – stefanB

+0

@stefan: Bạn không thể gọi các nhà thầu. Một lớp không có hàm tạo mặc định phải được khởi tạo trong danh sách khởi tạo, giống như các thành viên const. – GManNickG

1

Sự khác biệt thực sự là cách trình biên dịch gcc tạo mã máy và đặt bộ nhớ xuống. Giải thích:

  • (phase1) Trước khi init body (bao gồm danh sách init): trình biên dịch cấp phát bộ nhớ cần thiết cho lớp. Lớp học còn sống!
  • (giai đoạn 2) Trong phần thân init: vì bộ nhớ được cấp phát, mọi phép gán giờ đây cho biết một phép toán trên biến đã được khởi tạo/'khởi tạo'.

Chắc chắn có nhiều cách khác để xử lý các thành viên loại const. Nhưng để giảm bớt cuộc sống của họ, các nhà biên dịch trình biên dịch gcc quyết định thiết lập một số quy tắc

  1. thành viên loại const phải được khởi tạo trước cơ thể init.
  2. Sau phase1, mọi thao tác ghi chỉ hợp lệ đối với các thành viên không liên tục.
1

Chỉ có một cách để khởi tạo phiên bản cơ sở và biến thành viên không tĩnh và đang sử dụng danh sách trình khởi tạo.

Nếu bạn không chỉ định biến thành viên cơ sở hoặc không tĩnh trong danh sách khởi tạo của hàm dựng của bạn thì thành viên hoặc cơ sở đó sẽ được khởi tạo mặc định (nếu thành viên/cơ sở là loại lớp không phải POD hoặc mảng các loại lớp không phải POD) hoặc không được khởi tạo khác.

Khi cơ thể của nhà xây dựng được nhập, tất cả các cơ sở hoặc thành viên sẽ được khởi tạo hoặc không được khởi tạo (nghĩa là chúng sẽ có giá trị không xác định). Không có cơ hội trong cơ thể của nhà xây dựng để tác động đến cách chúng được khởi tạo.

Bạn có thể gán giá trị mới cho các thành viên trong phần thân của hàm tạo nhưng không thể gán cho const thành viên hoặc thành viên của loại lớp đã được gán không thể gán và không thể từ chối tham chiếu.

Đối với các loại được xây dựng và một số loại do người dùng xác định, việc gán trong phần thân của hàm tạo có thể có cùng tác dụng như khởi tạo với cùng giá trị trong danh sách khởi tạo.

Nếu bạn không đặt tên thành viên hoặc cơ sở trong danh sách khởi tạo và thực thể đó là tham chiếu, có loại lớp không có hàm tạo mặc định được người dùng khai báo là const đủ điều kiện và có loại POD hoặc loại lớp POD hoặc mảng loại lớp POD có một thành viên đủ điều kiện là const (trực tiếp hoặc gián tiếp), sau đó chương trình bị hỏng.

0

Nếu bạn viết danh sách trình khởi tạo, bạn làm tất cả trong một bước; nếu bạn không viết một danh sách initilizer, bạn sẽ thực hiện 2 bước: một cho khai báo và một cho asign giá trị.

5

Tôi nghĩ liên kết này http://www.cplusplus.com/forum/articles/17820/ đưa ra một giải thích tuyệt vời - đặc biệt là đối với những người mới sử dụng C++.

Lý do tại sao danh sách intialiser hiệu quả hơn là bên trong phần thân của hàm tạo, chỉ các phép gán diễn ra, chứ không phải khởi tạo. Vì vậy, nếu bạn đang xử lý một kiểu không có sẵn, hàm tạo mặc định cho đối tượng đó đã được gọi trước khi phần thân của hàm tạo đã được nhập vào. Bên trong phần thân hàm tạo, bạn đang gán một giá trị cho đối tượng đó.

Thực tế, đây là lệnh gọi hàm dựng mặc định, sau đó gọi đến toán tử gán bản sao. Danh sách khởi tạo cho phép bạn gọi trực tiếp hàm tạo bản sao và đôi khi điều này có thể nhanh hơn đáng kể (nhớ lại rằng danh sách khởi tạo trước phần thân của hàm tạo)

0

Có sự khác biệt giữa danh sách khởi tạo và câu lệnh khởi tạo trong hàm tạo . Hãy xem xét bên dưới mã:

#include <initializer_list> 
#include <iostream> 
#include <algorithm> 
#include <numeric> 

class MyBase { 
public: 
    MyBase() { 
     std::cout << __FUNCTION__ << std::endl; 
    } 
}; 

class MyClass : public MyBase { 
public: 
    MyClass::MyClass() : _capacity(15), _data(NULL), _len(0) { 
     std::cout << __FUNCTION__ << std::endl; 
    } 
private: 
    int _capacity; 
    int* _data; 
    int _len; 
}; 

class MyClass2 : public MyBase { 
public: 
    MyClass2::MyClass2() { 
     std::cout << __FUNCTION__ << std::endl; 
     _capacity = 15; 
     _data = NULL; 
     _len = 0; 
    } 
private: 
    int _capacity; 
    int* _data; 
    int _len; 
}; 

int main() { 
    MyClass c; 
    MyClass2 d; 

    return 0; 
} 

Khi MyClass được sử dụng, tất cả các thành viên sẽ được khởi tạo trước khi tuyên bố đầu tiên trong một constructor thực thi.

Nhưng, khi MyClass2 được sử dụng, tất cả các thành viên không được khởi tạo khi câu lệnh đầu tiên trong một hàm tạo được thi hành.

Trong trường hợp sau, có thể có vấn đề hồi quy khi ai đó thêm một số mã vào một hàm tạo trước khi một thành viên nào đó được khởi tạo.

Các vấn đề liên quan