析构函数是如何被调用四次的?
自动遍历链表
这是一种常见的递归形式。
链表是一种递归数据结构。
我认为您可能有兴趣在(自制和简单的)单链表中查看更多递归示例。
这是 ctor(简化版)。参数 a_max 指定要创建多少节点元素。
LMBM::Node::Node(uint8_t a_max) :
m_nodeId (++M_nodeCount),
m_next (0), // linked list
//... other node initializers items
{
if(a_max > 1)
{
uint8_t nmax = static_cast<uint8_t>(a_max - 1);
m_next = new Node(nmax); // recurse create another Node
dtbAssert(m_next)(a_max); // confirm
}
else // (1 >= a_max)
{
dtbAssert(1 == a_max)(a_max); // at least one node
// all requested nodes created
}
if (1 == m_nodeId) //i.e. the 1st node
{
M_firstNode = this; // capture list anchor to static
M_MAX_THREADS = a_max; // capture list size to static
// ... a few more actions
}
} // LMBM::Node::Node(uint8_t a_max)
以上内容是从运行代码中提取的,但我现在认为此代码不适合发布,因为当 new 失败时(出于任何原因)代码会断言。虽然我的 dtbAssert 提供调试器支持,但它不适合客户端使用。
是的,这里没有循环。
也许当你习惯它时,简单易用是递归的典型特征。
ctor 使用简单的 new 扩展列表(通常被许多喜欢智能指针的同行不鼓励)。
每个节点都被分配一个唯一的 m_node_id 以帮助调试。
创建此列表的调用很简单:
LMBM::Node nodes(LMBM::Node::DEFAULT_MAX_NODES);
main 中的代码(带有限制检查)可以为更大或更小的列表传入用户值。
dtor 与您的博客发现非常相似(尽管顺序相反)
LMBM::Node::~Node(void)
{
if(m_next) // delete objects and list
delete m_next; // recurse down the list
// ... clean up actions, if any
m_nodeId = 0;
} // Node::~Node(void)
这个 dtor 首先旋转到列表的末尾,然后在展开堆栈时进行清理和删除活动。
此 init() 方法再次使用递归初始化一些资源(信号量等),并首先完成最后一个节点的 init()。
void LMBM::Node::init(void)
{
if(m_next)
m_next->init(); // tail first
// 1. ALLOCATE resource, such as a semaphore
m_semIn = new Sem_t; // create 1 semaphore per thread
dtbAssert(m_semIn)(m_nodeId);
//std::cout << "m_semIn " << m_nodeId << " = " << (void*)m_semIn << "\n";
// 2. INITIALIZE default Sem_t ctor is ok
} // void LMBM::Node::init(void)
(这里也没有循环。)
仅供参考 - LMBM::Sem_t 有 4 行 C++ 代码,并通过 Linux API 包装单个 Posix 进程信号量,设置为本地模式(未命名,未共享)。
事实证明,这个 Posix 信号量确实适用于 std::threads 以及 posix 线程 - 这是我在这里测试的东西之一。
这里是 startApp()。除了所使用的递归之外,我不会展示更多内容......在我截取这段代码的示例中,一个线程执行每个节点的活动。
void LMBM::Node::startApp(void) // activate threads
{
if(m_next)
m_next->startApp(); // recurse to end of linked list
// 4. start my thread
m_thread = new std::thread (LMBM::Node::threadEntry, this);
dtbAssert(m_thread);
// ... confirm thread running state
} // void LMBM::Node::startApp(void) // activate threads
所有线程都以一种或另一种方式合作。
此时,我有N个节点;每个节点都有一个线程;而线程做什么可以由m_node_id决定;并且调试器 cout 可以使用具有 1..10 性质的线程 id 而不是系统 id 来扩充。