2011-09-24 28 views
36

Một câu hỏi nhỏ về tạo đối tượng. Nói rằng tôi có hai loại cổ phiếu này:Thứ tự các nhà thầu/người hủy cuộc gọi trong kế thừa

struct A{ 
    A(){cout << "A() C-tor" << endl;} 
    ~A(){cout << "~A() D-tor" << endl;} 
}; 

struct B : public A{ 
    B(){cout << "B() C-tor" << endl;} 
    ~B(){cout << "~B() D-tor" << endl;} 

    A a; 
}; 

và trong chính tôi tạo ra một thể hiện của B:

int main(){ 
    B b; 
} 

Lưu ý rằng B xuất phát từ A và cũng có một lĩnh vực loại A.

Tôi đang cố gắng tìm ra các quy tắc. Tôi biết rằng khi xây dựng một đối tượng đầu tiên gọi hàm khởi tạo của nó, và ngược lại khi hủy.

Còn trường nào (A a; trong trường hợp này)? Khi B được tạo, khi nào nó sẽ gọi hàm tạo của A? Tôi đã không xác định một danh sách khởi tạo, có một số loại danh sách mặc định? Và nếu không có danh sách mặc định? Và cùng một câu hỏi về phá hoại.

+3

Ví dụ của bạn có thể giải thích rõ hơn nếu thông điệp của bạn cho trình phá hủy khác với thông điệp của bạn cho hàm tạo. Ngoài ra, những gì đang có 'std :: sort' đang làm gì? – Tom

+0

Ngoài ra, khi thử nghiệm, so sánh việc xây dựng và phá hủy 'B b',' B * b = new B(); xóa b; 'và' A * a = new b(); delete a; '(So sánh những gì xảy ra khi bạn sử dụng từ khóa' virtual' cho hàm hủy của bạn, tức là 'virtual ~ A() {cout <<" A D-tor "<< endl;}') – Tom

+0

@Tom, Bạn là đúng. Loại bỏ lỗi trình biên dịch. – iammilind

Trả lời

67
  • Xây dựng luôn bắt đầu với cơ sở class. Nếu có nhiều cơ sở class es thì việc xây dựng bắt đầu với phần lớn cơ sở bên trái nhất. (lưu ý bên: Nếu có một thừa kế virtual thì nó được ưu tiên cao hơn).
  • Sau đó, các trường thành viên được tạo. Chúng được khởi tạo theo thứ tự chúng được khai báo
  • Cuối cùng, class tự được xây dựng
  • Thứ tự của các destructor là chính xác điều ngược lại

Không phân biệt của danh sách khởi tạo, thứ tự gọi sẽ như thế nào này: trường

  1. class A cơ sở của constructor
  2. class B 's tên a (loại class A) sẽ được xây dựng constructor
  3. nguồn gốc class B 's
+2

'Cuối cùng, bản thân lớp được xây dựng' - bạn đang nói về cơ thể của nhà xây dựng ở đây? – Wolf

+1

@Wolf: Tôi đoán có. – Ludwik

+0

@Wolf. Nó đề cập đến lớp dẫn xuất – MSD561

7

lớp cơ sở luôn được xây dựng trước khi các thành viên dữ liệu. Các thành viên dữ liệu được xây dựng theo thứ tự mà chúng được khai báo trong lớp. Lệnh này không liên quan gì đến danh sách khởi tạo. Khi một thành viên dữ liệu đang được khởi tạo, nó sẽ xem xét thông qua danh sách khởi tạo của bạn cho các tham số, và gọi hàm tạo mặc định nếu không có kết quả phù hợp. Destructors cho các thành viên dữ liệu luôn được gọi theo thứ tự ngược lại.

+1

+1 Ngắn gọn, nhưng tốt. Có lẽ một số đường typographic sẽ thu hút nhiều độc giả hơn. – Wolf

20

Giả sử không có ảo đa kế thừa/(mà làm phức tạp mọi thứ khá một chút) thì các quy tắc rất đơn giản:

  1. Bộ nhớ đối tượng được phân bổ
  2. Các constructor của lớp cơ sở được thực hiện, kết thúc bằng hầu hết có nguồn gốc
  3. Việc khởi thành viên được thực hiện
  4. Đối tượng trở thành một trường hợp thực sự của các lớp học
  5. đang
  6. Constructor được thực hiện

Một điều quan trọng cần nhớ là cho đến bước 4 đối tượng chưa phải là một thể hiện của lớp, bởi vì nó chỉ giành được tiêu đề này sau khi thực thi hàm khởi tạo. Điều này có nghĩa rằng nếu có một ngoại lệ được ném trong quá trình khởi tạo của một thành viên thì hàm hủy của đối tượng không được thực hiện, nhưng chỉ các phần đã được xây dựng (ví dụ: các thành viên hoặc các lớp cơ sở) sẽ bị hủy. Điều này cũng có nghĩa là nếu trong hàm tạo của một thành viên hoặc của một lớp cơ sở, bạn gọi bất kỳ hàm thành viên ảo nào của đối tượng mà thực hiện được gọi sẽ là cơ sở một, không phải là hàm cơ sở. Một điều quan trọng cần nhớ là thành viên được liệt kê trong danh sách khởi tạo sẽ được xây dựng theo thứ tự chúng được khai báo trong lớp, KHÔNG theo thứ tự chúng xuất hiện trong danh sách khởi tạo (may mắn, đủ trình biên dịch phù hợp nhất sẽ đưa ra cảnh báo nếu bạn liệt kê các thành viên theo một thứ tự khác với tuyên bố lớp học).

Cũng lưu ý rằng ngay cả nếu trong quá trình thực thi mã constructor đối tượng this đã đạt được đẳng cấp cuối cùng của nó (ví dụ đối với công văn ảo) destructor của lớp sẽ không được gọi là trừ khi các nhà xây dựng hoàn thành thực hiện của nó . Chỉ khi constructor hoàn thành việc thực thi instance object là một công dân hạng nhất thực sự trong các instance ... trước thời điểm đó chỉ là một "wanna-be instance" (mặc dù có class chính xác).

Tiêu hủy xảy ra theo thứ tự ngược chính xác: đầu tiên hủy đối tượng được thực hiện, sau đó nó mất lớp của nó (tức là từ điểm này trên đối tượng được coi là đối tượng cơ sở) thì tất cả các thành viên bị hủy theo thứ tự ngược lại và cuối cùng là quá trình phá hủy lớp cơ sở được thực hiện lên đến cha mẹ trừu tượng nhất. Đối với hàm khởi tạo nếu bạn gọi bất kỳ hàm thành viên ảo nào của đối tượng (trực tiếp hoặc gián tiếp) trong trình phá hủy cơ sở hoặc thành viên, việc thực thi sẽ là cha mẹ bởi vì đối tượng bị mất tiêu đề lớp khi lớp hủy hoàn thành.

+0

'bắt đầu từ phần lớn abstract' ở điểm 2: điều này có đúng không? – Wolf

+0

@Wolf: Có, điều này được đảm bảo. Nếu bạn có lớp 'D' có nguồn gốc từ' B' thì khi khởi tạo 'D' đầu tiên, hàm tạo của' B' được thực thi (trừu tượng nhất) và sau đó hàm tạo 'D' được thi hành. Trên thực tế đối tượng trở thành một 'D' thực (đối với các phương thức ảo) chỉ sau khi xây dựng tất cả các cơ sở và tất cả các thành viên khác đã được hoàn thành (đây là điểm phức tạp về việc gọi các phương thức ảo của' D' trong khi xây dựng một trong các các thành viên hoặc trong khi xây dựng một đối tượng phụ cơ sở). – 6502

+0

Nó không phải là trật tự xây dựng từ các căn cứ đến nguồn gốc mà tôi đã yêu cầu, đó là từ 'abstract' gây hiểu lầm, tôi nghĩ vậy. Như tôi đã học, một lớp trừu tượng là một lớp có ít nhất là trên phương thức ảo thuần túy. Xin hãy xem [ví dụ này] (http://coliru.stacked-crooked.com/a/2f5e712f8421d303) – Wolf

0

Output từ mã được sửa đổi là:

A() C-tor 
A() C-tor 
B() C-tor 
~B() D-tor 
~A() D-tor 
~A() D-tor 
3

class Base constructor luôn thực hiện first.so khi bạn viết một tuyên bố B b; constructor của A được gọi là đầu tiên và sau đó là lớp B constructor.therefore đầu ra từ các nhà thầu sẽ được trong một chuỗi như sau:

A() C-tor 
A() C-tor 
B() C-tor 
1
#include<iostream> 

class A 
{ 
    public: 
    A(int n=2): m_i(n) 
    { 
    // std::cout<<"Base Constructed with m_i "<<m_i<<std::endl; 
    } 
    ~A() 
    { 
    // std::cout<<"Base Destructed with m_i"<<m_i<<std::endl; 
    std::cout<<m_i; 
    } 

    protected: 
    int m_i; 
}; 

class B: public A 
{ 
    public: 
    B(int n): m_a1(m_i + 1), m_a2(n) 
    { 
    //std::cout<<"Derived Constructed with m_i "<<m_i<<std::endl; 
    } 

    ~B() 
    { 
    // std::cout<<"Derived Destructed with m_i"<<m_i<<std::endl; 
    std::cout<<m_i;//2 
    --m_i; 
    } 

    private: 
    A m_a1;//3 
    A m_a2;//5 
}; 

int main() 
{ 
    { B b(5);} 
    std::cout <<std::endl; 
    return 0; 
} 

Câu trả lời trong trường hợp này là 2531. làm thế nào constructo r được gọi là ở đây:

  1. B :: A (int n = 2) constructor được gọi
  2. B :: B (5) constructor được gọi
  3. B.m_A1 :: A (3) được gọi
  4. B.m_A2 :: A (5) được gọi

Cùng chiều Destructor được gọi là:

  1. B :: ~ B() được gọi là. tức là m_i = 2, giảm m_i xuống 1 trong A.
  2. B.m_A2 :: ~ A() được gọi. m_i = 5
  3. B.m_A1 :: ~ A() được gọi. m_i = 3 4 B :: ~ A() được gọi., m_i = 1

Trong ví dụ này, xây dựng m_A1 & m_A2 là không thích hợp về trật tự trật tự danh sách khởi tạo nhưng trật tự khai của họ.

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