简体   繁体   English

如何处理静态存储持续时间警告?

[英]How to deal with static storage duration warnings?

I'm a total newbie trying to learn C++ from a book.我是一个试图从书中学习 C++ 的新手。 The code below works and produces output as expected, but there are warnings on the two lines that define engine and randomInt : "Initialization of 'engine' with static storage duration may throw an exception that cannot be caught."下面的代码按预期工作并产生输出,但在定义enginerandomInt的两行上有警告:“使用静态存储持续时间初始化‘引擎’可能会引发无法捕获的异常。”

If I put lines 7 and 8 inside of main() , the warnings completely go away, but then engine and randomInt are not available to getNumber .如果我将第 7 行和第 8 行放在main() ,警告会完全消失,但是enginerandomIntgetNumber不可用。

I don't know how to fix the warnings.我不知道如何修复警告。 Also, perhaps more importantly, what is the proper approach for using randomInt in various places besides main() ?此外,也许更重要的是,除了main()之外,在各个地方使用randomInt的正确方法是什么? Is it proper to declare it in main() then pass it to functions as needed?main()声明它然后根据需要将它传递给函数是否合适? Somehow main() doesn't feel like the proper spot to be declaring these types of things.不知何故, main()感觉不像是声明这些类型的东西的合适地方。

I asked a question similar to this one earlier, but I'm still struggling to understand, and have provided an example that's hopefully useful.我之前问过一个与此类似的问题,但我仍在努力理解,并提供了一个希望有用的示例。

// Loosely based on Fig. 6.12: fig06_12.cpp, C++ How To Program, Ninth Edition

#include <iostream>
#include <iomanip>
#include <random>

std::default_random_engine engine( static_cast<unsigned int>( time(nullptr) ) );
std::uniform_int_distribution<unsigned int> randomInt( 1, 6 );

int getNumber();

int main() {
    for ( unsigned int counter = 1; counter <= 10; ++counter ) {
        std::cout << std::setw( 10 ) << randomInt( engine );
        if ( counter % 5 == 0 )
            std::cout << std::endl;
    }
    std::cout << getNumber() << std::endl;
    return 0;
}

int getNumber () {
    return randomInt( engine );
}

Output:输出:

/CLionProjects/Warning/cmake-build-debug/Warning
         3         5         6         3         3
         1         4         2         4         5
2

Process finished with exit code 0

One way to defer initialization of global variables such as the ones you are using is to wrap them in get -functions. 推迟全局变量初始化的一种方法是将它们包装在get -functions中。

std::default_random_engine& getEngine()
{
   // Initialized upon first call to the function.
   static std::default_random_engine engine(static_cast<unsigned int>(time(nullptr)));
   return engine;
}

std::uniform_int_distribution<unsigned int>& getRandomInt()
{ 
   // Initialized upon first call to the function.
   static std::uniform_int_distribution<unsigned int> randomInt(1, 6);
   return randomInt;
}

and then use getEngine() and getRandomInt() instead of using the variables directly. 然后使用getEngine()getRandomInt()而不是直接使用变量。

Using global variables is problematic, and it is common wisdom to avoid them unless they're absolutely necessary. 使用全局变量是有问题的,除非它们是绝对必要的,否则避免它们是常见的。 For details, see: 有关详情,请参阅:

Are global variables bad? 全球变量是否不好?

your question title also regards non-global-scope static storage duration variables (eg static locals of functions); 您的问题标题还涉及非全局范围的静态存储持续时间变量(例如函数的静态局部函数); these are less problematic but can also give you some headaches, especially in multi-threaded work. 这些问题较少,但也会给您带来一些麻烦,特别是在多线程工作中。

Bottom line: It's best to make your functions depending only on their parameters and have as few side-effects as is necessary. 底线:最好只根据参数调整功能,并尽可能减少副作用。 Let's do this with your getNumber() function: 让我们用你的getNumber()函数做到这一点:

template <typename Distribution>
typename Distribution::result_type getNumber (
    std::default_random_engine&  random_engine,
    Distribution&                distribution) 
{
    return distribution( random_engine );
}

int main()
{
    std::default_random_engine engine( static_cast<unsigned int>( time(nullptr) ) );
    std::uniform_int_distribution<unsigned int> randomInt( 1, 6 );

    for ( unsigned int counter = 1; counter <= 10; ++counter ) {
        std::cout << std::setw( 10 ) << randomInt( engine );
        if ( counter % 5 == 0 )
            std::cout << std::endl;
    }
    std::cout << getNumber( engine, randomInt ) << std::endl;
    return 0;
}

I agree with the other answers that global objects are problematic.我同意其他答案,即全局对象有问题。

However your question is specifically about initialization of global variables that can throw an exception and, in turn, that there is no C++ scope that can catch this exception (that I know of).但是,您的问题特别是关于可以引发异常的全局变量的初始化,反过来,没有 C++ 范围可以捕获此异常(我知道)。 (This warning is cert-err58-cpp in clang-tidy static analyzer.) (此警告是clang-tidy静态分析器中的cert-err58-cpp 。)

Another less know solution that worked in my experience is, if you can control the constructor of the class used as global, to make the constructor(s) invoked noexcept .根据我的经验,另一个noexcept解决方案是,如果您可以控制用作全局类的构造函数,则使构造函数调用noexcept

I think it has some advantages:我认为它有一些优点:

First, this forces you to think if you can avoid doing operations that can fail (eg allocation or open a file)首先,这迫使您思考是否可以避免执行可能失败的操作(例如分配或打开文件)

struct TypeUsedAsGlobal {
    explicit TypeUsedAsGlobal(int n) noexcept {
        ... // body of the constructor that cannot fail
    } catch(...) {}
...
};

and, second, if you cannot avoid them it will force you to handle the error in the constructor itself.其次,如果您无法避免它们,它将迫使您处理构造函数本身中的错误。

struct TypeUsedAsGlobal {
    explicit TypeUsedAsGlobal(int n) noexcept try : m_{n} {
        ... // body that can fail
    } catch(...) {
        ... // handle the error
        ... // cleanup as much as you can
        ... // print a message, and most likely std::terminate
    }
...
};

( try/catch can have smaller scope I think). (我认为try/catch范围可以更小)。

All things considered, this is not that bad, specially if the type is only used for global objects (not in scopes), because well, what can you do if a global object that is (hopefully) fundamental to the whole program cannot be constructed in the first place?考虑到所有因素,这还不错,特别是如果该类型仅用于全局对象(不在范围内),因为如果无法构造(希望)对整个程序至关重要的全局对象,您该怎么办首先? (eg because a file doesn't exist or if the resource that the global object represent is not really there or sufficient to the logic of the program.) (例如,因为文件不存在,或者如果全局对象表示的资源并不真正存在或不足以满足程序的逻辑。)

The benefits of this approach relative to the function with static variable is that your program fails right away and not after the first use of the global object an hour later.这种方法相对于带有static变量的函数的好处是你的程序会立即失败,而不是在一小时后第一次使用全局对象之后。 Of course it is not clear in general that one approach is better than the other, it depends.当然,一般情况下,一种方法是否优于另一种方法尚不清楚,这取决于。


As a final note : it is a bad idea to have a global random number generator.最后一点:拥有一个全局随机数生成器是一个坏主意。 Generators should be created when needed or passed down.生成器应在需要或传递时创建。 It is not safe to call the random number generator/distribution from different threads, but more importantly (mathematically) the reason is that you have to control the lifetime and state of the generators very carefully.从不同线程调用随机数生成器/分布是不安全的,但更重要的是(数学上)原因是您必须非常小心地控制生成器的生命周期和状态。

There is no one other to blame here than the authors of the book you are using, notorious for their bad practices.除了您正在使用的这本书的作者之外,没有其他人可以指责,因为他们的不良做法而臭名昭著。

See here for good alternatives The Definitive C++ Book Guide and List在这里查看好的替代品The Definitive C++ Book Guide and List

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM