2015-07-17 13 views
7

Tôi đang cố gắng tạo đối tượng service có thể chạy (tức là thực thi chức năng run()) trong một chuỗi riêng biệt. Đây là đối tượng phục vụC++ Thừa kế: Gọi phương thức ảo khi nó bị ghi đè

#include <boost/noncopyable.hpp> 
#include <atomic> 
#include <thread> 
#include <iostream> 

class service : public boost::noncopyable { 
public: 
    service() : stop_(false), started_(false) { } 

    virtual ~service() { 
    stop(); 
    if (thread_.joinable()) { 
     thread_.join(); 
    } 
    } 

    virtual void stop() { stop_ = true; } 

    virtual void start() { 
    if (started_.load() == false) { 
     started_ = true; 
     thread_ = std::thread([&]() { 
     run(); 
     }); 
    } 
    } 

protected: 
    virtual void run() = 0; 

    std::atomic<bool> stop_; 

    std::atomic<bool> started_; 

    std::thread thread_; 
}; 

Ta là tạo một lớp test mà kế thừa từ lớp trừu tượng này và được gọi trong main() chức năng

class test : public service { 
public: 
    test() : service() { 
    std::cout<< "CTOR" << std::endl; 
    start(); 
    } 

    ~test() { 
    std::cout<< "DTOR" << std::endl; 
    } 

protected: 
    void run() override { 
    std::cout << "HELLO WORLD" <<std::endl; 
    } 
}; 


int main() { 
    test test1; 
    return 0; 
} 

Bây giờ khi tôi thực hiện điều này, tại sao tôi nhận được một lỗi nói rằng pure virtual function called? Hàm run() được ghi đè rõ ràng trong lớp test. Whats tồi tệ hơn là nó chạy một cách chính xác đôi khi?

$ ./a.out 
CTOR 
DTOR 
pure virtual method called 
terminate called without an active exception 

$ ./a.out 
CTOR 
DTOR 
pure virtual method called 
terminate called without an active exception 

$ ./a.out 
CTOR 
DTOR 
pure virtual method called 
terminate called without an active exception 

$ ./a.out 
CTOR 
DTOR 
HELLO WORLD 

$ ./a.out 
CTOR 
DTOR 
pure virtual method called 
terminate called without an active exception 

Điều gì có thể xảy ra ở đây?

+1

bạn đang gọi hàm ảo 'start();' trong hàm tạo. Đó là lý do cho lỗi này. – Jagannath

+2

@Jagannath Điều đó hoàn toàn được xác định rõ ở đây và không phải là vấn đề. – Barry

+1

Không, không phải. Tại thời điểm này, đối tượng dẫn xuất đã được xây dựng. –

Trả lời

10

Làm theo, từng bước:

1) Bạn xây dựng đối tượng.

2) Bạn thực hiện đoạn mã sau:

if (started_.load() == false) { 
    started_ = true; 
    thread_ = std::thread([&]() { 
    run(); 
    }); 
} 

Các chủ đề mẹ ngay lập tức quay trở lại nơi mà nó main()ngay thoát và phá hủy đối tượng của bạn.

Dưới đây là lỗi của bạn:

  • Bạn đang không được bảo đảm rằng các chủ đề bắt đầu trong start() đang xảy ra để đạt được cuộc gọi đến run(), trên, trước khi thread cha mẹ chấm dứt quá trình này. Cả chuỗi con và chuỗi chủ đề đều chạy đồng thời.

Vì vậy, thỉnh thoảng, chuỗi chủ đề sẽ hủy đối tượng trước khi chuỗi con được đưa vào bánh răng và gọi run().

Tại thời điểm này, đối tượng có phương thức run() được gọi, đã bị hủy.

Hành vi chưa xác định.

Xác nhận bạn đang truy cập, mỗi lần một lần, là một kết quả có thể có của hành vi không xác định này.

+1

Không nên 'thread_.join()' trong lớp hủy hoại của lớp cơ sở đảm bảo rằng đối tượng không bị hủy trước khi luồng được chạy? – subzero

+2

@subzero Có ba bước sau đây: (A) Phân loại lớp dẫn xuất (B) Cơ thể chủ đề được thực thi (C) Lớp hủy lớp cơ sở. (A) chắc chắn xảy ra trước (C). Nhưng không có gì ngăn cản (A) xảy ra trước (B) trước (C), có nghĩa là vtable là unwound trước khi 'run()' được gọi trước khi 'join()' được gọi. – Barry

+1

@subzero 'đảm bảo rằng đối tượng không bị phá hủy' Trong lập trình MT, đó là công việc của bạn để kiểm soát tuổi thọ của đối tượng. Để nó lên đến trình biên dịch lúc nào cũng đưa ra các vấn đề như thế này. – PaulMcKenzie

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