C++ 模板成员函数、静态局部变量和销毁顺序
首先,为我的英语感到抱歉: 我设计了一个小库来管理我的程序使用的内存。它主要实现一种具有内存管理目的的共享对象。我有以下类(我省略了在这里不需要解释我的问题的其他成员和函数):C++ 模板成员函数、静态局部变量和销毁顺序,c++,templates,memory-management,c++11,static,C++,Templates,Memory Management,C++11,Static,首先,为我的英语感到抱歉: 我设计了一个小库来管理我的程序使用的内存。它主要实现一种具有内存管理目的的共享对象。我有以下类(我省略了在这里不需要解释我的问题的其他成员和函数): 一种称为数据包的参数化结构,包含类型为T的子对象和计数器: 一个名为pool的类,带有一个静态参数函数(当然还有其他代码),返回对ptrstack的引用: 用户类继承共享对象: struct internals\u t;//.cpp源代码中的定义(pImpl习惯用法)。 //`shared_obj`只使用指向
- 一种称为
的参数化结构,包含类型为数据包
的子对象和计数器:T
- 一个名为
的类,带有一个静态参数函数(当然还有其他代码),返回对pool
的引用:ptrstack
- 用户类继承共享对象:
struct internals\u t;//.cpp源代码中的定义(pImpl习惯用法)。
//`shared_obj`只使用指向其类型参数的指针。
类用户:公共共享对象
{
//成员函数访问其内部指针。
//'user_t'非常轻。它只包含一个指针,但可以用作
//普通物体。
//此外,我可以使用动态对象,而无需使用“新建”和“删除”
//因为内存是由shared_obj管理的
};
施工、分配/复制和销毁基本上执行以下操作:
- 每次要创建新的
共享对象时,都会在堆中创建一个
数据包*
- 每次复制
共享对象时,其相关数据包的计数器都会递增
- 每次调用
共享对象的析构函数时,其相关数据包的计数器都会递减
- 如果其关联数据包的计数器达到0,则通过以下方式将其关联指针推送到堆栈上:
pool::get().push(ptr);
- 因此,我重新定义了这个列表的第一点:当需要创建一个新的
,并且其类型的池为空时,将在堆中创建一个新数据包。如果池不是空的,我使用“就地”版本的新:数据包*
new(pool::get().top())T(args);
pool::get().pop();
因此,当程序退出时,销毁被委派,所有创建的内存都可以重用
解释够了
我的问题是:我需要类型为“user\t
”的任何其他静态对象(实际上,不同类型的不同对象继承了shared\u obj
),但它们中的任何一个都会产生内存泄漏,我认为原因如下:
- 静态对象是在创建其关联池之前创建的,因为每个类型的
的定义都是在定义之后实例化的:模板定义在使用的第一个指针中实例化为具体类型,而不是在编写模板的位置。因此,我的文件中的词法顺序与翻译单元中的定义顺序不同packet\u pool.get()
- 因此,我的静态对象在其关联池之后被销毁,因为静态对象的删除顺序与其构造顺序相反,这与其转换单元的定义顺序一致
- 当静态对象被销毁时,其关联的计数器达到0,并且它们试图被推入其已销毁的池中。这就造成了崩溃
- 翻译单元中实例化模板函数的具体“定义顺序”是什么?我的假设正确吗
- 问题是否也与池是本地静态函数或“get”函数本身是内联函数(因为它是在其类中定义的函数)有关?我的意思是,每个“池”(每个堆栈)是否有内部链接,对于不同的翻译单元中的相同类型,是否有不同的
池
template<class T>
struct packet
{
T t;
unsigned counter;
};
template<typename T>
struct ptrstack : public std::stack<T*>
{
~ptrstack()
{
while(!this->empty()) {
delete this->top();
this->pop();
}
}
};
class pool
{
template<class T>
using stack_tp = ptrstack<packet<T> >;
public:
template<class T>
static stack_tp<T>& get()
{
static stack_tp<T> pool;
return pool;
}
};
template<typename T>
class shared_obj
{ /* Members (see below) */ };
struct internals_t; // definition in a .cpp source code (pImpl idiom).
// `shared_obj` uses only pointer to its type parameter.
class user_t : public shared_obj<internals_t>
{
// member functions accessing its internal pointer.
// `user_t` is very light. It only mantains a pointer, but it could be used as
// a common object.
// moreover, I can work with dynamic objects without using new and delete
// since the memory is managed by shared_obj
};
pool::get<T>().push(ptr);
new (pool::get<T>().top()) T(args);
pool::get<T>().pop();