Về nội bộ và về mã được tạo, có sự khác biệt thực sự giữa:
MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}
và
MyClass::MyClass()
{
_capacity=15;
_data=NULL;
_len=0
}
cảm ơn...
Câu trả lời:
Giả sử rằng những giá trị đó là kiểu nguyên thủy, thì không, không có sự khác biệt. Danh sách khởi tạo chỉ tạo ra sự khác biệt khi bạn có các đối tượng là thành viên, vì thay vì sử dụng khởi tạo mặc định theo sau là gán, danh sách khởi tạo cho phép bạn khởi tạo đối tượng tới giá trị cuối cùng của nó. Điều này thực sự có thể nhanh hơn đáng kể.
Bạn cần sử dụng danh sách khởi tạo để khởi tạo các thành viên không đổi, tham chiếu và lớp cơ sở
Khi bạn cần khởi tạo thành viên hằng, các tham chiếu và truyền các tham số đến các hàm tạo lớp cơ sở, như đã đề cập trong phần chú thích, bạn cần 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 đủ) class / struct chứa tham chiếu:
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ở yêu cầu tham số (ví dụ: không có hàm tạo mặc định):
struct bb {};
struct dd
{
char c;
dd(char x) : c(x) {}
};
struct aa : dd
{
bb& rb;
aa(bb& b ) : dd('a'), rb(b) {}
};
_capacity
, _data
và _len
có các loại lớp không có hàm tạo mặc định có thể truy cập?
const
thành viên trong phần thân của phương thức khởi tạo, bạn phải sử dụng danh sách khởi tạo - các const
thành viên không phải là thành viên có thể được khởi tạo trong danh sách khởi tạo hoặc trong phần thân của phương thức khởi tạo.
Đúng. Trong trường hợp đầu tiên mà bạn có thể khai báo _capacity
, _data
và _len
như hằng số:
class MyClass
{
private:
const int _capacity;
const void *_data;
const int _len;
// ...
};
Điều này sẽ rất quan trọng nếu bạn muốn đảm bảo tính toàn const
vẹn của các biến phiên bản này trong khi tính toán các giá trị của chúng trong thời gian chạy, ví dụ:
MyClass::MyClass() :
_capacity(someMethod()),
_data(someOtherMethod()),
_len(yetAnotherMethod())
{
}
const
các cá thể phải được khởi tạo trong danh sách trình khởi tạo hoặc các kiểu bên dưới phải cung cấp các hàm tạo không tham số công khai (mà các kiểu nguyên thủy làm).
Tôi nghĩ liên kết này http://www.cplusplus.com/forum/articles/17820/ đưa ra lời giải thích tuyệt vời - đặc biệt là đối với những người mới làm quen với C ++.
Lý do tại sao danh sách intialiser hiệu quả hơn là bên trong phần thân 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 dựng sẵn, thì 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 đó.
Trên thực tế, đây là một lệnh gọi đến hàm tạo mặc định, theo sau là một lệnh gọi đến toán tử gán sao chép. Danh sách trình khởi tạo cho phép bạn gọi trực tiếp hàm tạo bản sao và điều này đôi khi có thể nhanh hơn đáng kể (nhớ lại rằng danh sách trình khởi tạo nằm trước phần thân của hàm tạo)
Tôi sẽ nói thêm rằng nếu bạn có các thành viên của loại lớp không có sẵn phương thức khởi tạo mặc định, thì khởi tạo là cách duy nhất để xây dựng lớp của bạn.
Một sự khác biệt lớn là phép gán có thể khởi tạo các thành viên của một lớp cha; trình 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.
Phụ thuộc vào các loại liên quan. Sự khác biệt tương tự giữa
std::string a;
a = "hai";
và
std::string a("hai");
trong đó biểu mẫu thứ hai là danh sách khởi tạo - nghĩa là, nó tạo ra sự khác biệt nếu kiểu yêu cầu đối số phương thức khởi tạo hoặc hiệu quả hơn với đối số phương thức khởi tạo.
Sự khác biệt thực sự nằm ở cách trình biên dịch gcc tạo mã máy và bố trí bộ nhớ. Giải thích:
Chắc chắn có nhiều cách khác để xử lý các thành viên kiểu const. Nhưng để giảm bớt cuộc sống của họ, những người viết trình biên dịch gcc quyết định thiết lập một số quy tắc
Chỉ có một cách để khởi tạo các cá thể lớp cơ sở và các biến thành viên không tĩnh và đó là sử dụng danh sách trình khởi tạo.
Nếu bạn không chỉ định biến cơ sở hoặc biến thành viên không tĩnh trong danh sách trình khởi tạo của phương thức khởi tạo 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à kiểu lớp không phải POD hoặc mảng của lớp không phải POD loại) hoặc không được khởi tạo nếu không.
Khi phần thân của hàm tạo đượ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 (tức là chúng sẽ có giá trị không xác định). Không có cơ hội nào trong phần thân hàm tạo để ảnh hưởng đến cách chúng nên được khởi tạo.
Bạn có thể gán các giá trị mới cho các thành viên trong phần thân hàm tạo nhưng không thể gán cho const
các thành viên hoặc các thành viên của loại lớp đã được thực hiện là không thể gán được và không thể gắn lại các tham chiếu.
Đối với các kiểu dựng sẵn và một số kiểu do người dùng xác định, việc gán trong phần thân hàm tạo có thể có tác dụng giống hệt như việc khởi tạo với cùng một giá trị trong danh sách trình khởi tạo.
Nếu bạn không đặt tên cho thành viên hoặc cơ sở trong danh sách trình khởi tạo và thực thể đó là tham chiếu, có loại lớp không có phương thức khởi tạo mặc định do người dùng khai báo có thể truy cập, const
đủ điều kiện và có kiểu POD hoặc là kiểu lớp POD hoặc mảng thuộc kiểu lớp POD có chứa một const
thành viên đủ điều kiện (trực tiếp hoặc gián tiếp) thì chương trình không được hình thành.
Nếu bạn viết danh sách trình khởi tạo, bạn thực hiện tất cả trong một bước; nếu bạn không viết danh sách trình khởi tạo, bạn sẽ thực hiện 2 bước: một để khai báo và một để gán giá trị.
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 một phương thức khởi tạo. Hãy xem xét mã dưới đây:
#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 câu lệnh đầu tiên trong một phương thức khởi tạo được thực thi.
Tuy nhiên, 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 phương thức khởi tạo được thực thi.
Trong trường hợp sau, có thể xảy ra sự cố 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 nhất định được khởi tạo.
Đây là một điểm mà tôi không thấy những người khác đề cập đến nó:
class temp{
public:
temp(int var);
};
Lớp tạm thời không có ctor mặc định. Khi chúng tôi sử dụng nó trong một lớp khác như sau:
class mainClass{
public:
mainClass(){}
private:
int a;
temp obj;
};
mã sẽ không biên dịch, vì trình biên dịch không biết cách khởi tạo obj
, vì nó chỉ có một ctor rõ ràng nhận giá trị int, vì vậy chúng ta phải thay đổi ctor như sau:
mainClass(int sth):obj(sth){}
Vì vậy, nó không chỉ là về const và tham chiếu!