[英]How to deal with static storage duration warnings?
我是一个试图从书中学习 C++ 的新手。 下面的代码按预期工作并产生输出,但在定义engine
和randomInt
的两行上有警告:“使用静态存储持续时间初始化‘引擎’可能会引发无法捕获的异常。”
如果我将第 7 行和第 8 行放在main()
,警告会完全消失,但是engine
和randomInt
对getNumber
不可用。
我不知道如何修复警告。 此外,也许更重要的是,除了main()
之外,在各个地方使用randomInt
的正确方法是什么? 在main()
声明它然后根据需要将它传递给函数是否合适? 不知何故, main()
感觉不像是声明这些类型的东西的合适地方。
我之前问过一个与此类似的问题,但我仍在努力理解,并提供了一个希望有用的示例。
// 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 );
}
输出:
/CLionProjects/Warning/cmake-build-debug/Warning
3 5 6 3 3
1 4 2 4 5
2
Process finished with exit code 0
推迟全局变量初始化的一种方法是将它们包装在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;
}
然后使用getEngine()
和getRandomInt()
而不是直接使用变量。
使用全局变量是有问题的,除非它们是绝对必要的,否则避免它们是常见的。 有关详情,请参阅:
您的问题标题还涉及非全局范围的静态存储持续时间变量(例如函数的静态局部函数); 这些问题较少,但也会给您带来一些麻烦,特别是在多线程工作中。
底线:最好只根据参数调整功能,并尽可能减少副作用。 让我们用你的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;
}
我同意其他答案,即全局对象有问题。
但是,您的问题特别是关于可以引发异常的全局变量的初始化,反过来,没有 C++ 范围可以捕获此异常(我知道)。 (此警告是clang-tidy
静态分析器中的cert-err58-cpp
。)
根据我的经验,另一个noexcept
解决方案是,如果您可以控制用作全局类的构造函数,则使构造函数调用noexcept
。
我认为它有一些优点:
首先,这迫使您思考是否可以避免执行可能失败的操作(例如分配或打开文件)
struct TypeUsedAsGlobal {
explicit TypeUsedAsGlobal(int n) noexcept {
... // body of the constructor that cannot fail
} catch(...) {}
...
};
其次,如果您无法避免它们,它将迫使您处理构造函数本身中的错误。
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
范围可以更小)。
考虑到所有因素,这还不错,特别是如果该类型仅用于全局对象(不在范围内),因为如果无法构造(希望)对整个程序至关重要的全局对象,您该怎么办首先? (例如,因为文件不存在,或者如果全局对象表示的资源并不真正存在或不足以满足程序的逻辑。)
这种方法相对于带有static
变量的函数的好处是你的程序会立即失败,而不是在一小时后第一次使用全局对象之后。 当然,一般情况下,一种方法是否优于另一种方法尚不清楚,这取决于。
最后一点:拥有一个全局随机数生成器是一个坏主意。 生成器应在需要或传递时创建。 从不同线程调用随机数生成器/分布是不安全的,但更重要的是(数学上)原因是您必须非常小心地控制生成器的生命周期和状态。
除了您正在使用的这本书的作者之外,没有其他人可以指责,因为他们的不良做法而臭名昭著。
在这里查看好的替代品The Definitive C++ Book Guide and List
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.