C++新手——关于引用遍历

/*给定的Object类会通过ref_保存对其他任意Object对象的引用。

  findRefs函数,收集指定root对象所有直接或间接引用到的对象(包括root本身)并保存到refs中。即找出a所有直接或间接的不重复引用关系元素!

*/

class Object  //Object类

{
public:
Object(const char* n):
name_(n)
{
}


std::string name_;
std::vector<Object*> ref_;  // 对其他对象的引用.
};


Object* init(){                    //初始化Object对象并且建立引用关系.
    #define NEWOBJ(OBJ) Object* OBJ = new Object(#OBJ);
    #define A_REF_B(A, B) A->ref_.push_back(B);

NEWOBJ(a);
NEWOBJ(b);
NEWOBJ(c);
NEWOBJ(d);
NEWOBJ(e);
NEWOBJ(f);
NEWOBJ(g);

A_REF_B(a, b);
A_REF_B(b, a);
A_REF_B(a, c);
A_REF_B(c, c);
A_REF_B(c, d);
A_REF_B(c, d);
A_REF_B(d, a);
A_REF_B(d, e);
A_REF_B(e, f);
A_REF_B(e, g);

/*该块函数可以通过宏定义来理解:首先NEWOBJ()函数通过指针指向创建了一个通过“*OBJ”引用对象的空容器vector,然后再根据引用关系函数A_REF_B()来往里面添加              add不同引用对象的指针,说白了就是往空的容器中通过“名”首指针引用关系建立起来的——“指针元素”。添加add方法是宏定义了ref_.push_back();具体就是:
          a.ref_.push_back(b);
 b.ref_.push_back(a);
 a.ref_.push_back(c);
 ......
   那么在实际的内存空间上又是怎样的呢?我们可以这样理解:vector a里面存放了地址指针元素b、c,接着是vector b中存放了地址指针元素a,依次递序。对于相同的引             用(如:A_REF_B(c, c);/A_REF_B(c, d);),只是覆盖了一次而已,并不影响结果。
         a={b,c};
 b={a};
 c={c,d};
 d={a,e};
 e={f,g};
*/

return a;
}

void findRootRefs(Object* root, std::set<Object*>& refs){

/*该块函数是此程序的核心部分,重要的语句有两条:

           ① refs.find(root->ref_[i]) != refs.end()

           ② findRootRefs(root->ref_[i],refs);


     ①我们先来理解第一条语句:该语句的核心部分是set集合中find()函数的使用,find()函数的功能是什么呢?MSDN中的说法是:

                  c++ stl容器set成员函数:find()--返回一个指向被查找到元素的迭代器。
 

        返回一个元素的迭代器?这实在是很难理解,特别是对于刚刚接触C++或者对C++了解不深的同学来说,根本理解不了。那么,我们换个角度,从该函数在程序中所表                 现出来的性质来看,我们就不难看出find()函数的功能到底是什么了!就我的理解,简单的来说,find()函数的功能就是在当前的set集合中去寻找所要查询的元素否“存                   有子节点”,因为set集合本身就是“红黑二叉树”;判断其是否有“迭代”,无非就是判断该元素是否是“父节点”,那么返回的就是该元素的“父节点指针型类型的迭代器”。

                

                我这样说,只是为了大家理解find()函数的功能,并不完全正确!明白是怎么回事就行了!接下来,这句语句就不难理解了:


                在当前集合中查找root所指向的该元素的“迭代器”(是否有子节点)是否不为空。(具体的描述,等会对着代码跑一遍就更加清楚了)。


              ②我们再来看第二条语句:首先,很明显这是一个“函数递归”。这也是整个程序的核心!

          理解了上面两句语句,那么整个函数块的功能也就出来了,我们对着代码跑一遍:
    
         <1>.函数进入时,接收了“参数root指针型引用对象”,其实就是指针引用对象a(由语句Object* root = init()可知,init()返回的就是a);以及一个“指针型引用对象set集合refs”。
 <2>.for循环语句首次循环时,i<root->ref_.size(),其实就是i<a->ref_.size(),由给出的引用关系我们可以知道指针对象a、b、c、d、e、f、g所引用的元素size分别是2、1、3、2、0、0;但是for循环语句的跳出却不仅仅由i的大小比较判断来退出,还需要continue来判定。
 <3>.接着是if(refs.size()==0)的判断,此时set集合refs为空,执行语句refs.insert(root),即refs.insert(a);继续:
    
 refs.insert(root->ref_[i]);  →refs.insert(a->ref_[0]),即refs.insert(b)
 findRootRefs(root->ref_[i],refs);  →findRootRefs(b,refs)

 <4>.for开始第二次循环,此时refs.size()已经不为0了,执行else:
         refs.find(root->ref_[i]) != refs.end();  →refs.find(b->ref_[0]),即refs.find(a);在refs中元素b是有引用关系的,也就是说b是个“父节点指针型类型的迭代器”(因为b又引用了a)


                         因此continue跳出当前for循环。这里要注意的是,此时通过continue跳出所回到的起点是:refs.insert(root->ref_[i]);,并且这里的i是i++之后的值,即:refs.insert(a->ref_[1])→refs.insert(c),继续:


 findRootRefs(root->ref_[i],refs);  →findRootRefs(c,refs)

 <5>.for开始第三次循环,此时refs中已经插入了元素a、b、c,执行else,refs.find(c->ref_[0]) != refs.end()/refs.find(c) != refs.end(),由引用关系A_REF_B(c, c)、A_REF_B(c, d)可知,c也是个“父节点指针型类型的迭代器”;因此跳出当前循环,继续:

 refs.insert(c->ref_[1])  →refs.insert(d)
 findRootRefs(root->ref_[i],refs)  →findRootRefs(d,refs)

 程序的往后步骤与以上类似,通过递归的方法对各个引用对象元素的vector中的指针对象进行“父节点指针型类型的迭代器”判断,从而直接或间接的得到元素a的引用对象指针元素。


 值得注意的是,虽然该程序采用了递归这种方法,运用少量的代码、比较复杂的逻辑完成了冗长的遍历;但是在C++中递归这种方法或者说“一次性递归”是不推荐使用的,原因是C++执行函数时,函数的局部变量大多是在栈上创建的;函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率虽然很高,但是分配的内存容量却十分有限;造成的结果是什么呢?对了,造成的结果就是C++被大多数code monkey所诟病的一个主要缺陷“内存泄露”或者“栈溢出”!作为C++初学者来说,看到“stack overflow”这种运行错误几乎已经“习以为常”了!更让人“欲哭无泪”的是,“内存泄露”或者“栈溢出”这种运行错误,程序debug是找不到错误到底是由哪一处的代码错误引起的,特别是前者!原因很简单,因为问题无非就是“内存指向不存在/错误(内存泄露,往往是指针问题造成的)”或者“内存不够用了(栈溢出)”。所以,递归的深度不宜过大(windows栈默认的内存大小是1M);通常情况下,我们一般采用“while+小递归”的方式来解决问题。

 因此,下面给出了该题的另一种解法!程序的核心是“模拟堆栈”,避免了递归!
*/

for (int i=0;i<root->ref_.size();i++)
{
if (refs.size()==0)
{
refs.insert(root);
}
else 
{
if (refs.find(root->ref_[i]) != refs.end())
{
continue;
}
}

refs.insert(root->ref_[i]);
findRootRefs(root->ref_[i],refs);
}


     /*stack.push_back(root);
while(stack.size())
{
Object* top = stack.back();
if(refs.find(top) == refs.end())
{
refs.insert(top);
for(size_t i = 0; i < top->ref_.size(); i++)
stack.push_back(top->ref_[i]);
}

else

                {

stack.pop_back();

                }

}*/

}

int main(){

   Object* root = init();
std::set<Object*> refs;
std::set<Object*>::iterator it;

     /*std::vector<Object*> stack;*/

findRootRefs(root, refs);

for(it=refs.begin(); it!=refs.end();it++){
printf(((*it)->name_).c_str());
cout <<endl;
}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值