cpp*_*cpp 2 c++ global-variables c++11
全局变量通常不被鼓励。C++ 标准库中具有外部链接的全局变量背后的原理是什么?
extern变量真的只是声明而不是定义吗?
std::call_once一个例子是声明具有mutex.h
外部链接的线程局部全局变量的 gcc 实现:
extern __thread void* __once_callable;
extern __thread void (*__once_call)();
Run Code Online (Sandbox Code Playgroud)
在
/// @cond undocumented
# ifdef _GLIBCXX_HAVE_TLS
// If TLS is available use thread-local state for the type-erased callable
// that is being run by std::call_once in the current thread.
extern __thread void* __once_callable;
extern __thread void (*__once_call)();
// RAII type to set up state for pthread_once call.
struct once_flag::_Prepare_execution
{
template<typename _Callable>
explicit
_Prepare_execution(_Callable& __c)
{
// Store address in thread-local pointer:
__once_callable = std::__addressof(__c);
// Trampoline function to invoke the closure via thread-local pointer:
__once_call = [] { (*static_cast<_Callable*>(__once_callable))(); };
}
~_Prepare_execution()
{
// PR libstdc++/82481
__once_callable = nullptr;
__once_call = nullptr;
}
_Prepare_execution(const _Prepare_execution&) = delete;
_Prepare_execution& operator=(const _Prepare_execution&) = delete;
};
# else
// Without TLS use a global std::mutex and store the callable in a
// global std::function.
extern function<void()> __once_functor;
extern void
__set_once_functor_lock_ptr(unique_lock<mutex>*);
extern mutex&
__get_once_mutex();
// RAII type to set up state for pthread_once call.
struct once_flag::_Prepare_execution
{
template<typename _Callable>
explicit
_Prepare_execution(_Callable& __c)
{
// Store the callable in the global std::function
__once_functor = __c;
__set_once_functor_lock_ptr(&_M_functor_lock);
}
~_Prepare_execution()
{
if (_M_functor_lock)
__set_once_functor_lock_ptr(nullptr);
}
private:
// XXX This deadlocks if used recursively (PR 97949)
unique_lock<mutex> _M_functor_lock{__get_once_mutex()};
_Prepare_execution(const _Prepare_execution&) = delete;
_Prepare_execution& operator=(const _Prepare_execution&) = delete;
};
# endif
Run Code Online (Sandbox Code Playgroud)
__once_callable&__once_call已声明但未定义,那么它们在哪里定义?我没有在devtoolset-11without的标准库头文件中找到它们的定义extern。它们是在源文件中定义的吗?确实,作为一般经验法则,全局变量的使用应该非常小心。这是因为在大型程序中,几乎不可能跟踪程序的哪些部分修改了变量,并且很快就无法理解和调试正在发生的事情(更多原因可以在这个问题的答案中找到)。
也就是说,全局变量是一个合法的工具,只要小心就可以使用。这些甚至可能有一些优势,例如在性能方面。基本上,如果您确保严格限制修改变量的位置数量,则可以使用全局变量。在这种情况下,该变量是标准库的内部变量,只能在mutex文件中访问,并且不应由任何其他代码访问,因此这就是为什么它没问题。
至于为什么在这种特定情况下他们必须使用全局变量,坦率地说,因为没有其他选择。该变量用于包装该函数中的可调用对象并将其传递给该__gthread_once()函数(第 907 行)。__gthread_once()只接受指向全局函数的指针,不带任何参数作为参数(此处__once_proxy)。为了__once_proxy()调用“可调用”,它必须存储在某个已知的位置。如果没有任何参数,唯一的选择是使用全局变量。
基本上,您可以在三种类型的内存中分配变量:堆、堆栈和可执行文件的静态分配变量区域(.bss)。“全局”变量在后面分配。全局变量的地址由链接器(或操作系统加载器)直接填充到可执行代码中。虽然堆栈或堆上变量的地址取决于很多因素,并且要访问那里的特定内容,它的地址必须作为参数(指针)传递,但如上所述不__once_proxy()接收任何参数,这就是堆栈和堆不接收任何参数的原因这里有一个选项。
extern变量(声明与定义)关于您问题的第二部分 - 是的extern仅声明一个变量。定义必须存在于某个地方。对于这些变量,正如mutex.cc@Jason 在他的回答中指出的那样。
| 归档时间: |
|
| 查看次数: |
334 次 |
| 最近记录: |