2012-02-22 49 views
9

Tôi có một chức năng mà tôi muốn chạy bất cứ khi nào thoát khỏi chương trình của tôi:Làm thế nào để bạn chạy một hàm trên lối ra trong C++

void foo() { 
    std::cout<< "Exiting" << std::endl; 
} 

Làm thế nào để đăng ký nó được chạy bất cứ khi nào chương trình tồn tại, bất kể khi nào và tại sao nó thoát - do tín hiệu, lệnh exit(), v.v ...?

+0

Không thể xảy ra trong mọi trường hợp - một số tín hiệu đưa ra quá trình ngay lập tức cho dù bạn có muốn hay không. (ví dụ: tràn ngăn xếp) –

+0

Hệ điều hành nào? –

Trả lời

26

Bạn có thể sử dụng hòn đảo có tên std::atexit chức năng trong cstdlib tiêu đề:

#include <cstdlib> 

void exiting() { 
    std::cout << "Exiting"; 
} 

int main() { 
    std::atexit(exiting); 
} 

Hệ thống sẽ duy trì một chồng các chức năng đăng ký với atexit và gọi cho họ mỗi người theo thứ tự ngược lại đăng ký của họ khi một trong hai exit chức năng được gọi, hoặc chương trình trả về từ main. Bạn có thể đăng ký ít nhất 32 chức năng theo cách này.

+0

Điều đó không được gọi sau khi một tín hiệu hoặc một cuộc gọi đến 'abort' /' terminate'. (Nói: đăng ký một chức năng được gọi là ** chấm dứt quá trình bình thường **) –

+1

@BillyONeal đó là lý do tại sao tôi nói "khi chức năng' thoát 'được gọi hoặc chương trình trả về từ 'main'" –

+0

Thở, sai lầm của tôi :) –

3

Bạn có thể đặt nó vào trình phá hủy của một lớp với một cá thể chung.

class SomeGlobalStuff { 
    ~SomeGlobalStuff() { 
      foo(); 
    } 
    static SomeGlobalStuff instance; 
}; 
// putting this in a single compilation unit. 
SomeGlobalStuff SomeGlobalStuff::instance instance; 

Nhưng cũng giống như bất kỳ phương pháp nào khác, bạn phải nhớ rằng bạn không thể sử dụng bất kỳ dữ liệu nào nếu bạn không thể che giấu nó vẫn tồn tại. Việc sắp xếp các đối tượng toàn cục được thực hiện theo thứ tự tùy ý, vì vậy về cơ bản, bạn không thể sử dụng lệnh std :: cout trong hàm foo(). atexit() là tồi tệ hơn trong vấn đề này, bởi vì cho dù nó thực hiện trước hoặc sau khi phá hủy các đối tượng toàn cầu phụ thuộc vào trình biên dịch và tùy chọn trình biên dịch.

Và dù sao, bạn vẫn phải xử lý tín hiệu chính xác. Bạn phải chọn các tín hiệu để xử lý và không xử lý (bạn có thể không muốn xử lý SIGSEGV). Bạn không thể thoát khỏi xử lý tín hiệu. Và hãy nhớ rằng các tín hiệu có thể làm gián đoạn chương trình của bạn bất kỳ lúc nào (trừ khi được che dấu) để cấu trúc dữ liệu của bạn có thể ở trạng thái tùy ý, ở giữa bản cập nhật.

+0

Tôi tin rằng có ngôn ngữ trong tiêu chuẩn nói rằng 'cout' là hợp lệ ở khắp mọi nơi (được định nghĩa để được xây dựng trước) nhưng tôi không thể tìm thấy nó ngay bây giờ. Điều này cũng không hoạt động đối với các tín hiệu, 'abort',' terminate', v.v. –

+0

@BillyONeal: 'abort' tăng tín hiệu (mà bạn phải chọn để xử lý hay không, giống như tôi đã nói),' terminate' là hoàn toàn vấn đề khác. Ngoài ra, có thể 'std :: cout' luôn tồn tại, nhưng hành động' abort() 'được định nghĩa để tăng' SIGABRT' "và có thể bao gồm một nỗ lực để tạo' fclose() 'trên tất cả các luồng đang mở". fclose (stdout) sẽ ngăn chặn std :: cout hoạt động. – BatchyX

+0

Chỉ trên các hệ thống có thứ như 'SIGABRT'. 'fclose (stdout)' có thể hoặc không thể gây ra 'std :: cout' để ngừng hoạt động, tùy thuộc vào CRT của bạn. Tiêu chuẩn không xác định mối quan hệ (nếu có) giữa các luồng đầu vào/đầu ra/lỗi chuẩn của bảng điều khiển và xử lý tệp đối tác C của chúng. Khi tôi nói rằng 'cout' luôn hợp lệ, nghĩa là nó không bị ảnh hưởng bởi' thứ tự khởi tạo của các đối tượng tĩnh không phải cục bộ được định nghĩa trong các đơn vị dịch khác nhau là quy tắc undefined'. Có lẽ tôi hiểu lầm câu trả lời của bạn. –

1

Cách duy nhất (trong các hệ điều hành giống Unix và Unix) để lấy lại quyền kiểm soát sau khi thoát khỏi quá trình là wait(2) cho nó. Ngắn của một powerfail, hạt nhân hoảng loạn, hoặc buộc khởi động lại, điều này sẽ làm việc:

#include <sys/types.h> 
#include <sys/wait.h> 
#include <iostream> 

int AtExit() { 
    pid_t pid = fork(); 
    if(pid < 0) return pid; 
    if(pid == 0) return pid; 
    pid = waitpid(pid, 0, 0); 
    return pid; 
} 

int main() { 
    if(AtExit()) { 
    std::cout << "Exiting\n"; 
    return 0; 
    } 

    std::cout << 7 << "\n"; 
} 
+1

Bạn đang di chuyển vấn đề ở nơi khác: điều gì sẽ xảy ra nếu tôi giết chương trình này? Như được viết,^C trên thiết bị đầu cuối điều khiển sẽ gửi SIGINT cho cả cha và con. – BatchyX

+0

Đây là một câu trả lời thực sự sáng tạo (mặc dù nó có vấn đề nhận xét ghi chú rằng^C sẽ nhấn cả hai). Bạn có thể bỏ qua^C trong trường hợp này nếu muốn nhưng bạn sẽ không thể bỏ qua^\. – IanPudney

1

Tôi trả lời là người dùng Linux, nhưng tất cả điều này nên áp dụng cho các cửa sổ.

Tôi đã có câu hỏi tương tự này, vì vậy hy vọng tôi có thể tổng hợp các câu trả lời trước đó và thêm hai xu của tôi.

Tín hiệu và abort(): ^C^Z có thể bị "chặn" để gọi chức năng của bạn trước khi thoát, có lẽ với exit(). Các tín hiệu SIGQUIT AKA ^\SIGKILL không bị chặn đột quỵ. Đây là một ví dụ để sử dụng tiêu đề csignal và một lambda C++.

#include <iostream> 
#include <csignal> 
#include <cstdlib> 

using namespace std; 

int main() 
{ 
    //signal requires lam take an int parameter 
    //this parameter is equal to the signals value 
    auto lam = 
     [] (int i) { cout << "aborting" << endl; exit(0); }; 

    //^C 
    signal(SIGINT, lam); 
    //abort() 
    signal(SIGABRT, lam); 
    //sent by "kill" command 
    signal(SIGTERM, lam); 
    //^Z 
    signal(SIGTSTP, lam); 


    while(1) 
    { 
    } 

    return 0; 
} 

Thoát: Vì tôi đã sử dụng exit() trong các ví dụ ở trên, cần lưu ý ở đây. Nếu chức năng đang chạy là một chức năng dọn dẹp chỉ cần chạy một lần, có lẽ một biến tĩnh has_run có thể được sử dụng. Hoặc trong ví dụ trên, raise() tín hiệu mà bạn không thể chặn. Nhưng những người có xu hướng đi kèm với bãi lõi mà chỉ cảm thấy bẩn. Lựa chọn của bạn, ở đây. Một ví dụ sau

#include <cstdlib> 
#include <iostream> 

using namespace std; 

int main() 
{ 
    //called with no parameters 
    auto lam = []() { cout << "at exit"; }; 

    atexit(lam); 

    return 0; 
} 

Hãy lưu ý rằng C++ 11 thêm một quick_exit trong đó có một kèm at_quick_exit mà hành động tương tự như trên.Nhưng với quick_exit không có công việc làm sạch nào được thực hiện. Ngược lại, với exit các trình phá hủy đối tượng được gọi và luồng C bị đóng, chỉ với các biến lưu trữ tự động không bị xóa.

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