2010-06-27 19 views
95

Giả sử tôi có một lớp học với các bộ ghi riêng tư ptr, name, pname, rname, crnameage. Điều gì xảy ra nếu tôi không tự khởi tạo chúng? Dưới đây là một ví dụ:Các thành viên lớp C++ được khởi tạo như thế nào nếu tôi không làm điều đó một cách rõ ràng?

class Example { 
    private: 
     int *ptr; 
     string name; 
     string *pname; 
     string &rname; 
     const string &crname; 
     int age; 

    public: 
     Example() {} 
}; 

Và sau đó tôi làm:

int main() { 
    Example ex; 
} 

như thế nào các thành viên khởi tạo ở cũ? Điều gì sẽ xảy ra với con trỏ? Do stringint nhận được 0-intialized với các nhà thầu mặc định string()int()? Điều gì về các thành viên tham khảo? Ngoài ra những gì về tham chiếu const?

Tôi nên biết điều gì khác?

Có ai biết hướng dẫn về các trường hợp này không? Có lẽ trong một số cuốn sách? Tôi có thể truy cập thư viện của trường đại học với rất nhiều sách C++.

Tôi muốn tìm hiểu để tôi có thể viết các chương trình tốt hơn (không có lỗi). Mọi phản hồi sẽ giúp ích!

+1

Để biết các khuyến nghị về sách, hãy xem http://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list –

+0

Mike, ow, ý tôi là chương từ một số cuốn sách giải thích nó. Không phải toàn bộ cuốn sách! :) – bodacydo

+0

Có lẽ sẽ là một ý tưởng hay khi đọc toàn bộ cuốn sách về ngôn ngữ bạn dự định lập trình. Và nếu bạn đã đọc và nó không giải thích được điều này, thì đó không phải là một cuốn sách hay. –

Trả lời

135

Thay cho khởi tạo rõ ràng, khởi tạo các thành viên trong lớp hoạt động giống hệt nhau để khởi tạo biến cục bộ trong hàm.

Đối với đối tượng, hàm tạo mặc định của chúng được gọi. Ví dụ: đối với std::string, hàm tạo mặc định đặt nó thành chuỗi rỗng. Nếu lớp của đối tượng không có một hàm tạo mặc định, nó sẽ là một lỗi biên dịch nếu bạn không khởi tạo nó một cách rõ ràng.

Đối loại nguyên thủy (con trỏ, ints, vv), họ là không khởi tạo - chúng chứa bất cứ điều gì rác tùy tiện đã xảy ra để được ở vị trí đó bộ nhớ trước đó.

Đối với tài liệu tham khảo (ví dụ std::string&), nó là bất hợp pháp không thể khởi tạo họ, và trình biên dịch của bạn sẽ phàn nàn và từ chối để biên dịch mã như vậy. Tài liệu tham khảo phải luôn được khởi tạo.

Vì vậy, trong trường hợp cụ thể của bạn, nếu chúng không được khởi tạo một cách rõ ràng:

int *ptr; // Contains junk 
    string name; // Empty string 
    string *pname; // Contains junk 
    string &rname; // Compile error 
    const string &crname; // Compile error 
    int age; // Contains junk 
+3

+1. Cần lưu ý rằng, theo định nghĩa chuẩn nghiêm ngặt, các thể hiện của các kiểu nguyên thủy cùng với nhiều thứ khác (bất kỳ vùng lưu trữ nào) đều được coi là các đối tượng *. – stinky472

+0

Đúng, nhưng nếu OP đã đọc tiêu chuẩn, anh ta sẽ không hỏi câu hỏi, vì vậy tôi không nghĩ rằng nó khó hiểu khi sử dụng * đối tượng * trong ý nghĩa lập trình hướng đối tượng chung của một loại lớp nào đó, như trái ngược thành kiểu nguyên thủy. :) –

+5

"Nếu lớp của đối tượng không có hàm tạo mặc định, nó sẽ là lỗi biên dịch nếu bạn không khởi tạo rõ ràng" đó là ** sai **! Nếu một lớp không có một hàm tạo mặc định, nó được gán một hàm dựng mặc định * mặc định * rỗng. – Wizard

7

Nếu bạn ví dụ lớp được khởi tạo trên ngăn xếp, nội dung của các thành viên vô hướng vô hướng là ngẫu nhiên và không xác định.

Đối với trường hợp tổng thể, các thành viên vô hướng chưa được khởi tạo sẽ bị xóa.

Đối với các thành viên là bản thân các thể hiện của các lớp, các hàm tạo mặc định của chúng sẽ được gọi, do đó đối tượng chuỗi của bạn sẽ được khởi tạo.

  • int *ptr; // uninitialized con trỏ (hoặc zeroed nếu toàn cầu)
  • string name; // constructor gọi, khởi tạo với chuỗi rỗng
  • string *pname; // uninitialized con trỏ (hoặc zeroed nếu toàn cầu)
  • string &rname; // lỗi biên dịch nếu bạn không thể khởi tạo số này
  • const string &crname; // lỗi biên dịch nếu bạn không khởi tạo được
  • này// giá trị vô hướng, uninitialized và ngẫu nhiên (hoặc zeroed nếu toàn cầu)
+0

Tôi đã thử nghiệm và có vẻ như 'tên chuỗi' trống sau khi khởi tạo lớp trên ngăn xếp. Bạn có chắc chắn về câu trả lời của mình không? – bodacydo

+1

chuỗi sẽ có một hàm tạo cung cấp một chuỗi rỗng theo mặc định - Tôi sẽ làm rõ câu trả lời của tôi –

+0

@ bodacydo: Paul là đúng, nhưng nếu bạn quan tâm đến hành vi này, nó không bao giờ đau để được rõ ràng. Ném nó vào danh sách khởi tạo. – Stephen

4

Uninitialized thành viên không tĩnh sẽ chứa dữ liệu ngẫu nhiên. Trên thực tế, họ sẽ chỉ có giá trị của vị trí bộ nhớ mà họ được chỉ định.

Tất nhiên đối với các tham số đối tượng (như string) hàm tạo của đối tượng có thể thực hiện khởi tạo mặc định.

Trong ví dụ của bạn:

int *ptr; // will point to a random memory location 
string name; // empty string (due to string's default costructor) 
string *pname; // will point to a random memory location 
string &rname; // it would't compile 
const string &crname; // it would't compile 
int age; // random value 
0

Thành viên với một constructor sẽ có constructor mặc định của họ kêu gọi khởi động.

Bạn không thể phụ thuộc vào nội dung của các loại khác.

0

Nếu nó là trên stack, các nội dung của thành viên chưa được khởi tạo mà không có nhà xây dựng riêng của họ sẽ được ngẫu nhiên và không xác định . Ngay cả khi nó là toàn cầu, nó sẽ là một ý tưởng tồi để dựa vào chúng được zeroed ra. Cho dù đó là trên stack hay không, nếu một thành viên có constructor riêng của nó, mà sẽ được gọi để khởi tạo nó.

Vì vậy, nếu bạn có chuỗi * pname, con trỏ sẽ chứa rác ngẫu nhiên. nhưng đối với tên chuỗi, hàm tạo mặc định cho chuỗi sẽ được gọi, cho bạn một chuỗi rỗng. Đối với các biến kiểu tham chiếu của bạn, tôi không chắc chắn, nhưng nó có lẽ sẽ là một tham chiếu đến một số đoạn bộ nhớ ngẫu nhiên.

21

Trước tiên, hãy để tôi giải thích danh sách mem-initializer-danh sách là gì. Một mem-initializer-list là một danh sách bằng dấu phẩy của mem-initializer s, trong đó mỗi mem-initializer là một tên thành viên tiếp theo (, tiếp theo là một biểu-list, tiếp theo là một ) . biểu thức danh sách là cách thành viên được xây dựng. Ví dụ, trong

static const char s_str[] = "bodacydo"; 
class Example 
{ 
private: 
    int *ptr; 
    string name; 
    string *pname; 
    string &rname; 
    const string &crname; 
    int age; 

public: 
    Example() 
     : name(s_str, s_str + 8), rname(name), crname(name), age(-4) 
    { 
    } 
}; 

các mem-initializer-list của các nhà xây dựng người dùng cung cấp, không có đối số là name(s_str, s_str + 8), rname(name), crname(name), age(-4). Đây mem-initializer-list có nghĩa là thành viên name được khởi tạo bởi the std::string constructor that takes two input iterators, thành viên rname được khởi tạo với một tham chiếu đến name, các crname thành viên được khởi tạo với một const-tham chiếu đến name, và các thành viên age được khởi tạo với giá trị -4.

Mỗi hàm tạo có danh sách khởi tạo mem- riêng của mình và các thành viên chỉ có thể được khởi tạo theo thứ tự quy định (về cơ bản thứ tự các thành viên được khai báo trong lớp).Do đó, các thành viên của Example chỉ có thể được khởi tạo theo thứ tự: ptr, name, pname, rname, crnameage.

Khi bạn không chỉ định một mem-initializer của một thành viên, tiêu chuẩn C++ nói:

Nếu đơn vị là thành viên dữ liệu không tĩnh ... của kiểu lớp ..., đơn vị được khởi tạo mặc định (8.5). ... Nếu không, thực thể không được khởi tạo.

Tại đây, vì name là thành viên dữ liệu không tĩnh của kiểu lớp, nó là mặc định khởi tạo nếu không khởi tạo cho name được quy định trong mem-initializer-list. Tất cả các thành viên khác của Example không có loại lớp, vì vậy chúng không được khởi tạo.

Khi tiêu chuẩn cho biết rằng chúng không được khởi tạo, điều này có nghĩa là chúng có thể có giá trị bất kỳ. Vì vậy, vì mã trên không khởi tạo pname, nó có thể là bất cứ điều gì.

Lưu ý rằng bạn vẫn phải tuân thủ các quy tắc khác, chẳng hạn như quy tắc tham chiếu phải luôn được khởi tạo. Nó là một lỗi trình biên dịch để không khởi tạo tài liệu tham khảo.

8

Bạn cũng có thể khởi tạo dữ liệu thành viên tại thời điểm bạn khai báo:

class another_example{ 
public: 
    another_example(); 
    ~another_example(); 
private: 
    int m_iInteger=10; 
    double m_dDouble=10.765; 
}; 

tôi sử dụng hình thức này khá nhiều độc quyền, mặc dù tôi đã đọc một số người coi đó là 'hình thức xấu', có lẽ bởi vì nó chỉ là mới được giới thiệu - tôi nghĩ trong C++ 11. Đối với tôi nó hợp lý hơn.

Một khía cạnh hữu ích khác cho quy tắc mới là cách khởi tạo thành viên dữ liệu là các lớp học của chính họ. Ví dụ giả sử rằng CDynamicString là một lớp đóng gói xử lý chuỗi. Nó có một hàm tạo cho phép bạn chỉ định giá trị ban đầu của nó CDynamicString(wchat_t* pstrInitialString). Bạn có thể sử dụng lớp này như một thành viên dữ liệu bên trong một lớp khác - nói một lớp đóng gói một giá trị sổ đăng ký trong trường hợp này lưu trữ một địa chỉ bưu điện. Để 'mã cứng' registry tên chìa khóa để mà điều này viết bạn sử dụng niềng răng:

class Registry_Entry{ 
public: 
    Registry_Entry(); 
    ~Registry_Entry(); 
    Commit();//Writes data to registry. 
    Retrieve();//Reads data from registry; 
private: 
    CDynamicString m_cKeyName{L"Postal Address"}; 
    CDynamicString m_cAddress; 
}; 

Note lớp chuỗi thứ hai nắm giữ địa chỉ bưu điện thực tế không có một khởi tạo nên constructor mặc định của nó sẽ được gọi về sáng tạo - có lẽ sẽ tự động đặt nó thành một chuỗi trống.

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