如何确定处理异常的位置 - 在函数范围内抛出还是在全局范围内?

时间:2021-02-24 20:24:25

I`m writing client-server app for windows using WinSock and I have class for server.
while initialising server I have such code:

我正在使用WinSock为Windows编写客户端 - 服务器应用程序,我有服务器类。初始化服务器我有这样的代码:


class Server {
    static const int MaxClients = 10;
    std::vector connections;
    CRITICAL_SECTION cs;
    int port;
    SOCKET ServerSocket;
    sockaddr_in ServerAddress;
    void init(); 
public:
    Server(int Port);
    void addConnection(const Client& newClient); 
    void closeConnection(int index); 
    void Listen();
    int size(); 
    /*virtual void ClientService();
    virtual void SendMsg(const std::string& msg);*/
    virtual ~Server();
};

void Server::init() {
    WSADATA wsaData;
    if(WSAStartup(MAKEWORD(2, 2), &wsaData))
        throw Exception("WinSock init failed");
    ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ServerSocket == INVALID_SOCKET)
        throw Exception("Socket failed to create!");
    ServerAddress.sin_family = AF_INET;
    ServerAddress.sin_addr.s_addr = htonl(INADDR_ANY); 
    ServerAddress.sin_port = htons(port);
    if(bind(ServerSocket,(sockaddr*)&ServerAddress,sizeof(ServerAddress)) == SOCKET_ERROR)
    {
        throw Exception("Binding failed");
        closesocket(ServerSocket); 
    }
}

where do I need to handle exceptions? Here, in init() private method (which is called from constructor) or in main function? Any rules existing where to place handlers for exceptions and what does it depend on?

我在哪里需要处理异常?在这里,在init()私有方法(从构造函数调用)或main函数中?是否有任何规则存在于异常处理程序的位置以及它依赖于什么?

3 个解决方案

#1


It depends on whether you're actually going to handle the exception, e.g. retry with a slightly different input, or decide to ignore the problem and proceed anyway (rarely appropriate, but can be useful). In this case, you may well want to catch the exception close to its source.

这取决于你是否真的要处理异常,例如用稍微不同的输入重试,或者决定忽略问题并继续进行(很少适当,但可能有用)。在这种情况下,您可能希望捕获靠近其源的异常。

In most cases, the only thing you can really do with an exception is log it and abort the operation (e.g. serve the user an error page in a web app, or pop up a friendly "this program has crashed, sorry" dialog and exit). In this case, your catch block is likely to be quite close to the top of the stack - e.g. in the main method.

在大多数情况下,你唯一可以用异常做的就是记录它并中止操作(例如,在web应用程序中为用户提供一个错误页面,或弹出一个友好的“这个程序已经崩溃,对不起”对话框并退出)。在这种情况下,您的catch块可能非常靠近堆栈顶部 - 例如在主要方法。

#2


The basic rule is that you only catch exceptions in that part of the code, where you can actually handle it. By handling it I mean that you can take some action based upon the exception thrown, for instance trying to create the socket another time, or trying to use a different configuration.

基本规则是您只捕获代码中那部分的异常,您可以在其中实际处理异常。通过处理它我的意思是你可以根据抛出的异常采取一些操作,例如尝试再次创建套接字,或尝试使用不同的配置。

Additionally, exceptions which cross library-boundaries need to be reviewed. Depending on the context you may want to catch them and rethrow a different type of exception, for instance to hide implementation details from the library client.

此外,需要审查跨越库边界的异常。根据上下文,您可能希望捕获它们并重新抛出不同类型的异常,例如从库客户机隐藏实现详细信息。

#3


As for the question about catching the exception in the scope of the function where it's thrown (ie., in the init() function) - it seldom makes sense to catch an exception in the scope where it's thrown. In essence that would mean that you've detected an error and know how to handle it (and are going to handle it), but that you're using an exception to perform nothing more than a transfer of control to another part of your function.

至于在函数范围内捕获异常的问题(即,在init()函数中) - 在抛出它的范围内捕获异常很少有意义。本质上,这意味着您已经检测到错误并知道如何处理它(并且将要处理它),但是您正在使用异常来执行除了将控制权转移到函数的另一部分之外的任何内容。

While there might be cases where this might make sense (though that would be a code smell that indicates the function is complex enough that it should be refactored), the logic for handling that error (since it is being handled) should use more common control flow techniques such as if statements or a break. Even the wildly unpopular goto might be preferable to throwing an exception only to have it handled in the same function.

虽然可能存在这种情况可能有意义的情况(虽然这可能是代码气味,表明函数足够复杂,应该重构),处理该错误的逻辑(因为它正在被处理)应该使用更常见的控制流程技术,如if语句或中断。即使是非常不受欢迎的goto也可能比抛出异常只是为了让它在同一个函数中处理。

#1


It depends on whether you're actually going to handle the exception, e.g. retry with a slightly different input, or decide to ignore the problem and proceed anyway (rarely appropriate, but can be useful). In this case, you may well want to catch the exception close to its source.

这取决于你是否真的要处理异常,例如用稍微不同的输入重试,或者决定忽略问题并继续进行(很少适当,但可能有用)。在这种情况下,您可能希望捕获靠近其源的异常。

In most cases, the only thing you can really do with an exception is log it and abort the operation (e.g. serve the user an error page in a web app, or pop up a friendly "this program has crashed, sorry" dialog and exit). In this case, your catch block is likely to be quite close to the top of the stack - e.g. in the main method.

在大多数情况下,你唯一可以用异常做的就是记录它并中止操作(例如,在web应用程序中为用户提供一个错误页面,或弹出一个友好的“这个程序已经崩溃,对不起”对话框并退出)。在这种情况下,您的catch块可能非常靠近堆栈顶部 - 例如在主要方法。

#2


The basic rule is that you only catch exceptions in that part of the code, where you can actually handle it. By handling it I mean that you can take some action based upon the exception thrown, for instance trying to create the socket another time, or trying to use a different configuration.

基本规则是您只捕获代码中那部分的异常,您可以在其中实际处理异常。通过处理它我的意思是你可以根据抛出的异常采取一些操作,例如尝试再次创建套接字,或尝试使用不同的配置。

Additionally, exceptions which cross library-boundaries need to be reviewed. Depending on the context you may want to catch them and rethrow a different type of exception, for instance to hide implementation details from the library client.

此外,需要审查跨越库边界的异常。根据上下文,您可能希望捕获它们并重新抛出不同类型的异常,例如从库客户机隐藏实现详细信息。

#3


As for the question about catching the exception in the scope of the function where it's thrown (ie., in the init() function) - it seldom makes sense to catch an exception in the scope where it's thrown. In essence that would mean that you've detected an error and know how to handle it (and are going to handle it), but that you're using an exception to perform nothing more than a transfer of control to another part of your function.

至于在函数范围内捕获异常的问题(即,在init()函数中) - 在抛出它的范围内捕获异常很少有意义。本质上,这意味着您已经检测到错误并知道如何处理它(并且将要处理它),但是您正在使用异常来执行除了将控制权转移到函数的另一部分之外的任何内容。

While there might be cases where this might make sense (though that would be a code smell that indicates the function is complex enough that it should be refactored), the logic for handling that error (since it is being handled) should use more common control flow techniques such as if statements or a break. Even the wildly unpopular goto might be preferable to throwing an exception only to have it handled in the same function.

虽然可能存在这种情况可能有意义的情况(虽然这可能是代码气味,表明函数足够复杂,应该重构),处理该错误的逻辑(因为它正在被处理)应该使用更常见的控制流程技术,如if语句或中断。即使是非常不受欢迎的goto也可能比抛出异常只是为了让它在同一个函数中处理。