如果构造函数抛出异常,那么有一个全局对象是否有意义?

时间:2021-10-16 11:41:16

I am asking this question for general coding guidelines:

我问这个问题的一般编码准则:

class A {
  A() { ... throw 0; }
};
A obj;  // <---global

int main()
{
}

If obj throws exception in above code then, it will eventually terminate the code before main() gets called. So my question is, what guideline I should take for such scenario ? Is it ok to declare global objects for such classes or not ? Should I always refrain myself from doing so, or is it a good tendency to catch the error in the beginning itself ?

如果obj在上面的代码中抛出异常,那么它最终会在main()被调用之前终止代码。所以我的问题是,对于这种情况,我应该采取什么指导方针?是否可以为此类类声明全局对象?我应该总是克制自己不这样做,还是应该在一开始就发现错误?

6 个解决方案

#1


4  

If you NEED a global instance of an object whose constructor can throw, you could make the variable static, instead:

如果您需要一个对象的全局实例,该对象的构造函数可以抛出,您可以使该变量保持静态,而不是:

A * f(){

   try {

      //lock(mutex);   -> as Praetorian points out
      static A a;
      //unlock(mutex);

      return &a;
   }
   catch (...){

      return NULL;
   }
}

int main() {

   A * a = f(); //f() can be called whenever you need to access the global

}

This would alleviate the problem caused by a premature exception.

这将减轻由不成熟的异常引起的问题。

EDIT: Of course, in this case the solution is 90% of the way to being a Singleton. Why not just fully turn it into one, by moving f() into A?

编辑:当然,在这种情况下,解决方案是成为单例的90%。为什么不把f()变成A呢?

#2


1  

No, you should not declare such objects global - any exception will be unhandled and very hard to diagnose. The program will just crash which means that it will have very poor (below zero) user experience and will be rather hard to maintain.

不,您不应该将此类对象声明为全局的——任何异常都将无法处理,并且很难诊断。这个程序将会崩溃,这意味着它将会有非常糟糕的用户体验(低于0),并且很难维护。

#3


0  

As @Kerrek SB has mentioned in the comments, the answer to this is dependent on the reasons that can cause your class to throw. If you're trying to acquire a system resource that might be unavailable, I feel you shouldn't declare a global object. Your program will crash as soon as the user tries to run it; needless to say, that doesn't look very good. If it can throw a std::bad_alloc or some such exception that is unlikely under normal circumstances (assuming you're not trying to allocate a few GB of memory) you could make a global instance; however, I would still not do that.

正如@Kerrek SB在评论中提到的,答案取决于导致类抛出的原因。如果您正在尝试获取可能不可用的系统资源,我认为您不应该声明全局对象。一旦用户试图运行程序,程序就会崩溃;不用说,这看起来不太好。如果它可以抛出std: bad_alloc或在正常情况下不太可能出现的某些异常(假设您没有尝试分配几GB内存),那么您可以创建一个全局实例;然而,我还是不会那样做。

Instead, you could declare a global pointer to the object, instantiate the object right at the beginning of main (before any threads have been spawned etc.) and point the pointer to this instance, then access it through the pointer. This gives your program a chance to handle exceptions, and maybe prompt the user to take some sort of remedial measures (like popping up a Retry button to try and reacquire the resource, for instance).

相反,您可以声明一个指向该对象的全局指针,在main(在任何线程生成等之前)初始化对象,并将指针指向该实例,然后通过指针访问它。这使您的程序有机会处理异常,并可能提示用户采取某种补救措施(例如,弹出重试按钮以尝试重新获取资源)。

#4


0  

Declaring a global object is fine, but the design of your class is insignificant, it lacks details to be compatible with practical needs and use.

声明全局对象是可以的,但是类的设计是无关紧要的,它缺乏与实际需求和使用兼容的细节。

#5


0  

One solution no one seems to have mentionned is to use a function try block. Basically, if the situation is that without the constructed object, the rest of your program won't work or be able to do anything useful, then the only real problem is that your user will get some sort of incomprehensible error message if the constructor terminates with an exception. So you wrap the constructor in a function try block, and generate a comprehensible message, followed by an error return:

一个似乎没有人提到的解决方案是使用函数try块。基本上,如果没有构造的对象,程序的其余部分将无法工作或无法做任何有用的事情,那么唯一的真正问题是,如果构造函数终止时出现异常,您的用户将得到某种难以理解的错误消息。因此,您将构造函数封装到一个函数try块中,并生成一个可理解的消息,然后是一个错误返回:

A::() try
    : var1( initVar1 )
    // ...
{
    //  Additional initialization code...
} catch ( std::exception const& ) {
    std::cerr << "..." << std::endl;
    exit(EXIT_FAILURE);
} catch (...) {
    std::cerr << "Unknown error initializing A" << std::endl;
    exit(EXIT_FAILURE);
}

This solution is really only appropriate, however, if all instances of the object are declared statically, or if you can isolate a single constructor for the static instances; for the non-static instances, it is probably better to propagate the exception.

但是,如果对象的所有实例都是静态声明的,或者您可以为静态实例隔离单个构造函数,那么这个解决方案实际上是适当的;对于非静态实例,最好传播异常。

#6


0  

Like @J T have said, you can write like this:

正如@J T所说,你可以这样写:

struct S {
  S() noexcept(false);
};

S &globalS() {
  try {
    static S s;
    return s;
  } catch (...) {
    // Handle error, perhaps by logging it and gracefully terminating the application.
  }
  // Unreachable.
}

Such scenario is quite a problem, please read ERR58-CPP. Handle all exceptions thrown before main() begins executing for more detail.

这样的场景是一个相当大的问题,请阅读ERR58-CPP。处理main()开始执行之前抛出的所有异常,以获得更多细节。

#1


4  

If you NEED a global instance of an object whose constructor can throw, you could make the variable static, instead:

如果您需要一个对象的全局实例,该对象的构造函数可以抛出,您可以使该变量保持静态,而不是:

A * f(){

   try {

      //lock(mutex);   -> as Praetorian points out
      static A a;
      //unlock(mutex);

      return &a;
   }
   catch (...){

      return NULL;
   }
}

int main() {

   A * a = f(); //f() can be called whenever you need to access the global

}

This would alleviate the problem caused by a premature exception.

这将减轻由不成熟的异常引起的问题。

EDIT: Of course, in this case the solution is 90% of the way to being a Singleton. Why not just fully turn it into one, by moving f() into A?

编辑:当然,在这种情况下,解决方案是成为单例的90%。为什么不把f()变成A呢?

#2


1  

No, you should not declare such objects global - any exception will be unhandled and very hard to diagnose. The program will just crash which means that it will have very poor (below zero) user experience and will be rather hard to maintain.

不,您不应该将此类对象声明为全局的——任何异常都将无法处理,并且很难诊断。这个程序将会崩溃,这意味着它将会有非常糟糕的用户体验(低于0),并且很难维护。

#3


0  

As @Kerrek SB has mentioned in the comments, the answer to this is dependent on the reasons that can cause your class to throw. If you're trying to acquire a system resource that might be unavailable, I feel you shouldn't declare a global object. Your program will crash as soon as the user tries to run it; needless to say, that doesn't look very good. If it can throw a std::bad_alloc or some such exception that is unlikely under normal circumstances (assuming you're not trying to allocate a few GB of memory) you could make a global instance; however, I would still not do that.

正如@Kerrek SB在评论中提到的,答案取决于导致类抛出的原因。如果您正在尝试获取可能不可用的系统资源,我认为您不应该声明全局对象。一旦用户试图运行程序,程序就会崩溃;不用说,这看起来不太好。如果它可以抛出std: bad_alloc或在正常情况下不太可能出现的某些异常(假设您没有尝试分配几GB内存),那么您可以创建一个全局实例;然而,我还是不会那样做。

Instead, you could declare a global pointer to the object, instantiate the object right at the beginning of main (before any threads have been spawned etc.) and point the pointer to this instance, then access it through the pointer. This gives your program a chance to handle exceptions, and maybe prompt the user to take some sort of remedial measures (like popping up a Retry button to try and reacquire the resource, for instance).

相反,您可以声明一个指向该对象的全局指针,在main(在任何线程生成等之前)初始化对象,并将指针指向该实例,然后通过指针访问它。这使您的程序有机会处理异常,并可能提示用户采取某种补救措施(例如,弹出重试按钮以尝试重新获取资源)。

#4


0  

Declaring a global object is fine, but the design of your class is insignificant, it lacks details to be compatible with practical needs and use.

声明全局对象是可以的,但是类的设计是无关紧要的,它缺乏与实际需求和使用兼容的细节。

#5


0  

One solution no one seems to have mentionned is to use a function try block. Basically, if the situation is that without the constructed object, the rest of your program won't work or be able to do anything useful, then the only real problem is that your user will get some sort of incomprehensible error message if the constructor terminates with an exception. So you wrap the constructor in a function try block, and generate a comprehensible message, followed by an error return:

一个似乎没有人提到的解决方案是使用函数try块。基本上,如果没有构造的对象,程序的其余部分将无法工作或无法做任何有用的事情,那么唯一的真正问题是,如果构造函数终止时出现异常,您的用户将得到某种难以理解的错误消息。因此,您将构造函数封装到一个函数try块中,并生成一个可理解的消息,然后是一个错误返回:

A::() try
    : var1( initVar1 )
    // ...
{
    //  Additional initialization code...
} catch ( std::exception const& ) {
    std::cerr << "..." << std::endl;
    exit(EXIT_FAILURE);
} catch (...) {
    std::cerr << "Unknown error initializing A" << std::endl;
    exit(EXIT_FAILURE);
}

This solution is really only appropriate, however, if all instances of the object are declared statically, or if you can isolate a single constructor for the static instances; for the non-static instances, it is probably better to propagate the exception.

但是,如果对象的所有实例都是静态声明的,或者您可以为静态实例隔离单个构造函数,那么这个解决方案实际上是适当的;对于非静态实例,最好传播异常。

#6


0  

Like @J T have said, you can write like this:

正如@J T所说,你可以这样写:

struct S {
  S() noexcept(false);
};

S &globalS() {
  try {
    static S s;
    return s;
  } catch (...) {
    // Handle error, perhaps by logging it and gracefully terminating the application.
  }
  // Unreachable.
}

Such scenario is quite a problem, please read ERR58-CPP. Handle all exceptions thrown before main() begins executing for more detail.

这样的场景是一个相当大的问题,请阅读ERR58-CPP。处理main()开始执行之前抛出的所有异常,以获得更多细节。