2010-01-02 43 views
6

Tôi thấy điều này lạ. Trong ctor của Sample_Base, tôi gọi bar() mà gọi nội bộ fun() là một hàm ảo thuần túy. Tôi nhận được lỗi "hàm ảo thuần túy" được gọi. Mà là tốt. Bây giờ, nếu tôi gọi fun() trực tiếp từ ctor của Sample_Base, tôi không nhận được lỗi đó. Tôi đã thử nó trên VC++ 2010 Beta 2 và trên g ++ 4.4.1 trên Ubuntu 9.10. Tôi đồng ý rằng việc thực hiện một chức năng ảo thuần túy, khác với destructor ảo thuần túy, là vô nghĩa. Nhưng, tôi hơi ngạc nhiên về hành vi này.Chức năng ảo thuần túy được gọi là lỗi

class Sample_Base 
{ 
public: 
    Sample_Base() 
    { 
     bar(); 
     // fun(); 
    } 
    /* This is code does not throw any error. 
    Sample_Base() 
    { 
     fun(); 
    } 
    */ 

    void bar() 
    { 
     fun(); 
    } 
    virtual void fun() = 0; 
    virtual ~Sample_Base(); 
}; 

Sample_Base::~Sample_Base() 
{ 

} 

void Sample_Base::fun() 
{ 
    std::cout << "Sample_Base::fun\n"; 
} 

class Sample_Derived : public Sample_Base 
{ 
public: 
    Sample_Derived() : Sample_Base() 
    { 
     fun(); 
    } 

    void fun() 
    { 
     std::cout << "Sample_Derived::fun\n"; 
    } 

    ~Sample_Derived() 
    { 

    } 
}; 

Trả lời

6

Khi bạn gọi hàm trực tiếp, vì bạn đang ở trong hàm tạo, trình biên dịch sẽ giải quyết loại tĩnh của đối tượng của bạn (Sample_Base) và gọi trực tiếp Sample_Base::fun(). Kể từ khi bạn cung cấp một thực hiện cho nó, trình biên dịch tìm thấy chức năng và nó hoạt động.

Khi bạn gọi nó gián tiếp, thông qua bar(), trình biên dịch phải sử dụng loại động, do đó, nó thực hiện cuộc gọi ảo được giải quyết khi chạy. Và nó không thành công, bởi vì nó gọi một hàm ảo thuần túy.

Vì vậy, sự khác biệt là tại thời điểm nó liên kết chức năng với cuộc gọi.

+0

[Tiêu chuẩn C++] (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4296.pdf) §10.4.6 nói rằng "một cuộc gọi ảo đến một chức năng ảo thuần túy ... đối với đối tượng đang được tạo ... không xác định ". Bạn có thể bình luận về điều đó? Tôi sẽ đọc nó mà gọi 'fun' trực tiếp cũng nên được undefined (ngay cả khi trình biên dịch không sản xuất một chương trình bị rơi trong trường hợp cụ thể này). Hay không phải là một cuộc gọi ảo trong bối cảnh đó (nếu vậy tại sao, tôi không thể tìm thấy nó trong tiêu chuẩn). – Xlea

+0

@Xlea Charles Bailey chỉ cho bạn các phần phù hợp của tiêu chuẩn trong câu trả lời của mình. Tâm trí bạn, ông đã sử dụng tiêu chuẩn C++ 03 trở lại sau đó, do đó, số phần sẽ khác nhau bây giờ, nhưng điều đó có thể hướng dẫn bạn. – Gorpik

1

Gọi hàm ảo sẽ không gọi hàm ghi đè trong lớp bắt nguồn. Gọi một hàm ảo thuần túy trong một hàm tạo hoặc hàm hủy là Hành vi không xác định.

Bạn có thể quan tâm đọc thisthis.

1

Tại thời gian thi công, khi các nhà xây dựng Sample_Base được gọi, đối tượng không được xây dựng hoàn chỉnh được nêu ra. Cụ thể, các bộ phận thuộc về Sample_Derived chưa được tạo cuộc gọi tới các chức năng ảo sẽ bị ghi đè bởi Sample_Derived sẽ không gọi triển khai trong Sample_Derived nhưng phiên bản được xác định trong Sample_Base. Và kể từ khi chức năng không có thực hiện, bạn nhận được một lỗi.

Để biết thêm thông tin và cách giải quyết khả thi cũng có thể xem this entry in the C++ FAQ Lite.

+0

Tôi đồng ý rằng đối tượng không được xây dựng hoàn chỉnh khi thanh đang được gọi trong Sample_Base, nhưng khi tôi gọi fun() từ hàm tạo của Sample_Base, tôi không nhận được bất kỳ lỗi nào. Đó là những gì tôi quan tâm. – Jagannath

+0

@Jagannath: Nhận hoặc không nhận được lỗi không phải là những gì bạn nên lo lắng về, bạn nên lo lắng về thực tế là thiết kế này là nguy hiểm. Bạn nên cố gắng tránh gọi các phương thức ảo từ constructor/destructor của bạn. –

+0

@Gal: Tôi biết thiết kế không chính xác và cần tránh gọi hàm ảo từ một hàm tạo. Tôi chỉ quan tâm đến sự khác biệt trong hành vi. Dù sao cũng cảm ơn. – Jagannath

4

Cung cấp định nghĩa cho hàm ảo thuần túy không nhất thiết là vô nghĩa. Đánh dấu một hàm ảo thuần túy có nghĩa là lớp kèm theo là trừu tượng và bất kỳ lớp nào xuất phát từ nó là trừu tượng trừ khi việc ghi đè cuối cùng cho hàm đó không phải là một hàm ảo thuần túy. Một hàm ảo thuần túy vẫn có thể được gọi thông qua một cuộc gọi không ảo rõ ràng.

Trong phần thân của hàm tạo lớp cơ sở (nhưng không phải từ ctor-initializer) phiên bản của hàm ảo được gọi thông qua cuộc gọi ảo là một định nghĩa trong chính lớp đó hoặc một trong các cơ sở của nó chứ không phải bất kỳ lớp ghi đè nó (mà chưa được xây dựng). Điều này được xác định rõ ràng trong 12.7 [class.cdtor]/3. Nó là hợp pháp để gọi một chức năng ảo thuần túy một cách rõ ràng trong một cơ quan nhà xây dựng (tức là sử dụng một vòng loại lớp rõ ràng) - mặc dù điều này sẽ yêu cầu chức năng để có một xác định - nhưng nó là hành vi không xác định để gọi một chức năng ảo tinh khiết thông qua một cuộc gọi ảo chỉ có thể từ hàm tạo hoặc hàm hủy của lớp trừu tượng. Điều này được xác định rõ ràng trong 10.4 [class.abstract]/6.

1

Hành vi này không được xác định, nó được xác định rõ ràng: các hàm ảo không ảo trong các hàm tạo và hàm hủy. Họ gọi phiên bản tĩnh của hàm. Nếu chức năng là ảo tinh khiết, điều này dẫn đến lỗi "cuộc gọi ảo thuần túy" nổi tiếng trong VC.

Tôi đã nhìn thấy một biến thể thú vị của điều này trong một chương trình đa luồng: một đối tượng đang bị hủy trên luồng A, trong khi chuỗi B đang cố gọi một hàm ảo. Không có lời gọi hàm ảo nào trong hàm tạo hoặc hàm hủy, nhưng chúng tôi vẫn bị lỗi với một cuộc gọi ảo thuần túy.

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