2015-06-11 20 views
10

Tôi đã cố gắng "sửa" ví dụ trong số this answer để minh họa cách một chức năng ảo thuần túy có thể được gọi.Tại sao đây không phải là một cuộc gọi của một chức năng ảo thuần túy?

#include <iostream> 
using namespace std; 

class A 
{ 
    int id; 
public: 
    A(int i): id(i) {} 
    int callFoo() { return foo(); } 
    virtual int foo() = 0; 
}; 

class B: public A 
{ 
public: 
    B(): A(callFoo()) {} 
    int foo() { return 3; } 
}; 

int main() { 
    B b; // <-- this should call a pure virtual function 
    cout << b.callFoo() << endl; 
    return 0; 
} 

Nhưng tôi nhận được không có lỗi runtime here (with C++ 4.9.2), nhưng sản lượng 3. Tôi đã cố gắng cùng với Borland C++ 5.6.4, nhưng có tôi nhận được một vi phạm truy cập. Tôi nghĩ rằng foo() nên được tinh khiết ảo trong cuộc gọi của các nhà xây dựng của lớp cơ sở.

Ai là người sai? Tôi có nên thử nhiều trình biên dịch hơn không? Tôi có đúng trong sự hiểu biết của tôi về các chức năng ảo không?

+4

Tôi sẽ không sử dụng kết quả thử nghiệm với Borland C++ để xem đoạn mã có hợp lệ hay không hoặc tuân thủ tiêu chuẩn;) – CoryKramer

+0

@CoryKramer Các chức năng ảo đang được sử dụng trong nhiều thập kỷ nay. – Wolf

+1

Tôi nhận thức được điều đó, tôi đã thực hiện một nhận xét táo bạo về một người vẫn đang sử dụng Borland IDEs – CoryKramer

Trả lời

14

Mã của bạn có hành vi không xác định: UB được gọi là hàm thành viên trên đối tượng (ngay cả đối tượng ảo) trước khi tất cả các lớp cơ sở của nó được khởi tạo. C++ 14 (n4140) 12.6.2/14, nhấn mạnh mỏ:

Chức năng thành viên (bao gồm chức năng thành viên ảo, 10.3) có thể được gọi cho đối tượng đang được xây dựng. Tương tự, đối tượng đang được xây dựng có thể là toán hạng của toán tử typeid (5.2.8) hoặc của dynamic_cast (5.2.7). Tuy nhiên, nếu các hoạt động này được thực hiện trong một ctor-initializer (hoặc trong một chức năng gọi trực tiếp hoặc gián tiếp từ một ctor-initializer) trước khi tất cả các mem-initializers cho các lớp học cơ sở đã hoàn thành, kết quả hoạt động không xác định. ...

ctor-initializer là toàn bộ danh sách sau đây :. mem-initializer là một phần tử của danh sách này.

+0

Đúng và quy tắc UB này được thiết lập tốt. Nhưng điều này chỉ di chuyển các nhức đầu - không có trình biên dịch cũng không kiểm tra mã tĩnh sẽ tìm thấy tất cả các lỗi. Tôi đã bị sốc rằng các trường hợp nội tuyến đơn giản như vậy đã không nhận được ngay lập tức bị từ chối. – Wolf

+0

@Wolf: Trình biên dịch [không bắt buộc phải thực hiện phân tích luồng kiểm soát đầy đủ] (http://blogs.msdn.com/b/oldnewthing/archive/2013/10/11/10455907.aspx), bởi vì điều đó có thể không thể làm tĩnh tại thời gian biên dịch. – Kevin

+0

@Kevin Thực hiện các chức năng ảo thuần túy để loại bỏ * chức năng ảo thuần túy được gọi là * tin nhắn có vẻ như lạm dụng với tôi, hoặc nó được thiết kế cho chính xác mục đích này? – Wolf

5

Tuyên bố B b; gọi hàm tạo mặc định là B.

Khi xây dựng B, không có gì liên quan đến B được xây dựng cho đến khi A được xây dựng hoàn chỉnh.

Vì vậy, khi cố gắng gọi callFoo(), hành vi không xác định vì bạn không thể dựa vào bảng v cho lớp B đang được thiết lập.

Tóm lại: hành vi gọi một hàm ảo thuần túy trong khi xây dựng lớp trừu tượng không được xác định.

+0

Tôi đoán đây sẽ là trường hợp vì. Vì vậy, có vẻ như tôi là một chút lười biếng với đề nghị "sửa chữa" của tôi. – Wolf

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