Đúc động trong hàm hủy


8

Mã này có hợp pháp không?

class Base1 {
};

class Base2 {
public:
    virtual ~Base2() {
        if (!dynamic_cast<Base1*>(this))
            std::cout << "aaaa" << std::endl;
    }
    Base2() {
    }
};

class MyClass: public Base1, public Base2 {
public:
    MyClass() {
    }
    virtual ~MyClass() {
        std::cout << "bbb" << std::endl;
    }
};

int main() {
    MyClass s;
    return 0;
}

Tôi thấy cả hai bản in nhưng tôi chỉ thấy một. Tôi đoán các diễn viên năng động là sai. Có thể thực hiện một kiểm tra của loại này?


Bạn có thể làm rõ những gì bạn đang cố kiểm tra? Base2 có muốn biết nếu nó là một cơ sở của lớp dẫn xuất cũng có Base1 không?
jtbandes

Vâng, tôi muốn kiểm tra trong Base2 nếu "này" là một đứa trẻ của base1 quá
greywolf82

"Nhưng tôi chỉ nên nhìn thấy một" tại sao? và tại sao bạn có nghi ngờ về tính hợp pháp?
idclev 463035818

1
@ greywolf82 oh xin lỗi tôi đã bỏ lỡ!
idclev 463035818

2
Không dynamic_casthành xử khác nhau trong các nhà xây dựng và phá hủy?
François Andrieux

Câu trả lời:


7

Có lẽ tôi đã tự mình tìm ra giải pháp, câu trả lời là không thể.

Từ viên đạn 6 của tài liệu cppreference.com :

Khi Dynamic_cast được sử dụng trong hàm tạo hoặc hàm hủy (trực tiếp hoặc gián tiếp) và biểu thức đề cập đến đối tượng hiện đang được xây dựng / hủy, đối tượng được coi là đối tượng dẫn xuất nhất. Nếu kiểu mới không phải là một con trỏ hoặc tham chiếu đến lớp riêng của hàm tạo / hàm hủy hoặc một trong các cơ sở của nó, thì hành vi không được xác định.

Xem thêm [class.cdtor] / 6 của tiêu chuẩn.

Vì tôi đang truyền tới Base1 trong hàm hủy Base2, nên hành vi này không được xác định.


1
[class.cdtor]/6, để tham khảo. Xin lỗi, không phải 5. Đã là 5 trong C ++ 17 (bản nháp N4659), có vẻ như /6bây giờ.
ChrisMM

@ChrisMM Đoạn đó dường như không đề cập đến hành vi không xác định được tham chiếu trong câu trả lời này. Trên thực tế tôi đã không thể tìm thấy hạn chế này ở bất cứ đâu trong tiêu chuẩn.
quả óc chó

Tôi nghĩ rằng cppreference đã viết nhầm "kiểu mới " khi nó phải là " biểu thức ". Đoạn tiêu chuẩn được liên kết nói rằng toán hạng cần phải có kiểu (tĩnh) của lớp của hàm hủy hoặc cơ sở của nó nếu nó đề cập đến đối tượng bị phá hủy. Trong mã câu hỏi thislà loại của hàm hủy, vì vậy tôi không thấy rằng UB áp dụng. Tuy nhiên, cùng một đoạn tiêu chuẩn nói rằng loại dẫn xuất nhất của đối tượng bị phá hủy sẽ được coi là lớp của hàm hủy, do đó hành vi được giải thích trong câu trả lời khác được quan sát.
quả óc chó

@walnut, chỉ là đăng đoạn văn tương đối từ tiêu chuẩn, không phải câu trả lời của tôi. Tôi đồng ý nó không phải là UB mặc dù.
ChrisMM

3

Tôi đồng ý với câu trả lời của @ j6t, nhưng đây là một lý do mở rộng với các tài liệu tham khảo tiêu chuẩn.

Hành vi đặc biệt của dynamic_castcác đối tượng đang được xây dựng và phá hủy được mô tả bởi [class.cdtor] / 5 của tiêu chuẩn C ++ 17 (bản nháp cuối cùng) và tương đương bởi các phiên bản tiêu chuẩn trước đó.

Cụ thể nó nói:

Khi a dynamic_­castđược sử dụng [...] trong một hàm hủy, [...], nếu toán hạng của tham chiếu dynamic_­castđến đối tượng đang được xây dựng hoặc phá hủy, đối tượng này được coi là một đối tượng dẫn xuất nhất có loại [ ...] Lớp hủy diệt. Nếu toán hạng của tham chiếu dynamic_­castđến đối tượng bị phá hủy [...] và kiểu tĩnh của toán hạng không phải là con trỏ tới hoặc đối tượng của lớp riêng của hàm hủy [hoặc] hoặc một trong các cơ sở của nó, thì Dynamic_cast sẽ dẫn đến hành vi không xác định.

Hành vi không xác định không áp dụng ở đây, vì toán hạng là biểu thức this, mà tầm thường có loại con trỏ đến lớp riêng của hàm hủy vì nó xuất hiện trong chính hàm hủy.

Tuy nhiên, câu đầu tiên nói rằng ý dynamic_castchí hành xử như thể *thislà một đối tượng xuất phát nhất của loại Base2và do đó, diễn viên Base1không bao giờ có thể thành công, vì Base2không xuất phát từ Base1dynamic_cast<Base1*>(this)sẽ luôn trả về một con trỏ rỗng, dẫn đến hành vi bạn đang thấy.


cppreference.com nói rằng hành vi không xác định xảy ra nếu loại đích của diễn viên không phải là loại của hàm hủy hoặc một trong các cơ sở của nó, thay vì áp dụng điều này cho loại toán hạng. Tôi nghĩ rằng đó chỉ là một sai lầm. Có lẽ việc đề cập đến " loại mới " trong gạch đầu dòng 6 được cho là nói " biểu hiện ", điều này sẽ làm cho nó phù hợp với cách giải thích của tôi ở trên.


2

Điều dynamic_castnày được xác định rõ trong tình huống này. Đúng là bạn quan sát cả hai dòng đầu ra.

Bạn đã sai khi cho rằng trong hàm hủy của Base2 thislà một lớp dẫn xuất. Tại thời điểm này, phần lớp dẫn xuất đã bị hủy, vì vậy nó không thể là lớp dẫn xuất nữa. Trong thực tế, vào thời điểm đó khi destructor của Base2chạy, đối tượng được trỏ đến bởi this chỉ có một Base2đối tượng. Vì Base2không liên quan đến Base1bất kỳ cách nào, nên dynamic_casttrả về một con trỏ null và điều kiện được nhập tương ứng.

Chỉnh sửa: Tiêu chuẩn nói :

Khi a dynamic_­castđược sử dụng trong hàm tạo [...] hoặc trong hàm hủy [...], nếu toán hạng của tham chiếu dynamic_­castđến đối tượng đang xây dựng hoặc hủy, đối tượng này được coi là đối tượng dẫn xuất nhất có loại của lớp xây dựng hoặc lớp hủy. Nếu toán hạng của tham chiếu dynamic_­castđến đối tượng đang được xây dựng hoặc phá hủy và kiểu tĩnh của toán hạng không phải là con trỏ tới hoặc đối tượng của lớp xây dựng hoặc lớp hủy của chính nó hoặc một trong các cơ sở của nó, dynamic_­castkết quả trong hành vi không xác định.

Toán hạng thisdùng để chỉ đối tượng bị phá hủy. Do đó, lớp của hàm hủy ( Base2) được coi là lớp có nguồn gốc nhiều nhất và đó là lý do tại sao đối tượng không liên quan đến loại đích ( Base1*) theo bất kỳ cách nào. Hơn nữa, kiểu tĩnh của toán hạng thisBase2* const, rõ ràng là một con trỏ trỏ đến lớp riêng của hàm hủy. Do đó, quy tắc về hành vi không xác định không áp dụng. Tóm lại, chúng tôi có hành vi được xác định rõ.


1
điều này mâu thuẫn với câu trả lời khác nói rằng (trích dẫn từ tiêu chuẩn) rằng mã có hành vi không xác định. Bạn cần một số lý lẽ tốt để phản đối điều đó, chỉ cần nói
idclev 463035818

1
@ trước đây là Unknown_463035818 Câu trả lời khác là trích dẫn từ cppreference, không phải là tiêu chuẩn. Điều đó có thể không rõ ràng. Đoạn tiêu chuẩn mà nó đề cập dường như không nói điều tương tự như cppreference.
quả óc chó

@walnut Tôi nghĩ rằng đó là một trích dẫn từ tiêu chuẩn trong khi nó đã được chỉnh sửa để trích dẫn từ cppref. Nhưng dù sao, câu trả lời là mâu thuẫn, một người có nguồn để sao lưu, người kia thì không, chỉ nói ...
idclev 463035818

@walnut Tôi đã tự kiểm tra và [class.cdtor]/6được đề cập trong câu trả lời khác giống như cppref: "Nếu toán hạng của Dynamic_cast đề cập đến đối tượng đang xây dựng hoặc phá hủy và kiểu tĩnh của toán hạng không phải là con trỏ tới hoặc đối tượng của hàm tạo hoặc Lớp riêng của hàm hủy hoặc một trong các cơ sở của nó, Dynamic_cast dẫn đến hành vi không xác định. "
idclev 463035818

1
Tôi đã thêm giải thích của tôi về tiêu chuẩn.
6
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.