c++递归中语句的执行顺序是怎样的?

案例一: 

  void recurs()
 
{
           statements1;//等待被执行的语句1
           if(test)
              recurs();//调用自身,体现递归思想
           statements2;//等待被执行的语句2
}   

        分析:这里用分层思想更便于理解,首先执行statement1语句,然后判断是否符合test条件,如果符合,进入下一层recurs()调用,直到判断不符合条件为止(到这里为止,我们下面的statement2语句一次也没有被执行)。然后这里执行最底层的statement2语句,并将程序控制权返回给上一层的函数,上一层的函数继续执行statement2语句,依次类推,知道将控制权返回到最初的函数手里结束。

但是我们肯定会有这样的疑问:为什么不是一条counting down紧接着一条Kaboom,而是先输出完所有的counting down才输出Kaboom呢?

下面贴一段代码辅助理解:(代码来源:C++ primer plus)

#include <iostream>
using namespace std;
void countdown(int n)
{
    cout<< "counting down..."<< n << endl;//statement1执行语句    
    if(n>0)
       countdown(n-1);//递归,调用下一层contdown()函数 
    cout<< ":Kaboom!"<< n <<endl;;//statement2执行语句    
}
 
int main()
{
     countdown(4);
     return 0;
}
 

从上图不难看出,对于测试语句1则是一口气执行到了头,对于测试语句2则是从最底层执行回到最高层(数字的排序就可看出来,语句一是从4到0,语句二是从0到4,正好相反)

 

案例二:

 

#include<iostream>
using namespace std;
//斐波那契数列 
int c(int n){
	if(n<=2)
	   return 1;
	else 
	   return c(n-2)+c(n-1);
}
int main(){
	cout<<c(5)<<endl;
}

 

但是当我们把n=1换成n=2的时候,程序输出结果又会发生什么变化?

 

 

#include<iostream>
using namespace std;
//斐波那契数列 
int fun(int n){
	if(n<=2)
	   return 2;
	else 
	   return fun(n-2)+fun(n-1);
}
int main(){
	cout<<fun(5)<<endl;
}

可以看到,结果竟然变成了原先的两倍,这其实和递归终止语句(if(n<=2)  return 1;)有关,我们可以画出递归二叉树来理清关系,

 

红圈部分就是递归结束的标志,每一支末尾都返回一个值,加起来就变成了结果,所以上面变化的原因就不难理解了。这也从侧面印证了递归终止语句不止用来判断递归是否结束,还在求值过程中发挥重要作用。

 

 

 

<think> 我们正在讨论C++中析构函数的执行顺序,特别是成员对象的析构顺序。根据引用[2]和引用[4]: 1. 析构函数的调用顺序与构造函数相反。 2. 首先执行自身的析构函数(即派生类自己的析构函数体)。 3. 然后按照与声明相反的顺序析构成员变量(即成员对象)。 4. 最后调用基类的析构函数。 引用[4]提到:“析构时先调用派生类的析构函数,再调用基类的析构函数。” 并且还提到:“采用初始化列表来解决基类成员和内嵌成员对象的初始化。... 子类新增自己的非成员对象的普通变量在子类的构造函数里初始化”。 关于成员变量的析构顺序,引用[2]明确指出:“2.执行成员变量的析构函数;” 并且顺序与构造顺序相反。构造顺序是按照成员变量在类中声明的顺序,所以析构的顺序就是声明的逆序。 另外,引用[5]中的示例代码也展示了这一点: 构造顺序:基类的静态成员(如果有)-> 基类的成员对象 -> 派生类的成员对象(按照声明顺序)-> 派生类构造函数 析构顺序:派生类析构函数 -> 派生类成员对象(按照声明的逆序)-> 基类成员对象 -> 基类(如果有静态成员,则静态成员在程序结束时析构) 注意:静态成员不属于任何一个对象,它们在程序开始前初始化,在程序结束时析构。所以对象析构时不会析构静态成员。 因此,对于非静态成员对象,在析构函数执行时的顺序如下: - 首先执行当前类的析构函数体(不包括成员对象的析构,这部分是自动的)。 - 然后按照成员变量在类中声明的相反顺序,依次调用每个成员对象的析构函数。 - 最后调用基类的析构函数,基类的析构函数也会按照同样的规则(先基类的析构函数体,然后基类的成员对象按声明逆序析构,再往上基类的基类)。 总结: 在没有虚继承的情况下,析构顺序为: 1. 当前派生类的析构函数体 2. 派生类的成员对象(按照声明的逆序析构) 3. 基类的析构函数(然后基类也会重复这个过程:基类的析构函数体 -> 基类的成员对象的逆序析构 -> 基类的基类的析构函数) 注意:如果存在多个基类(多重继承),那么基类析构的顺序与继承列表的顺序相反(即先继承的后析构)。但是题目中只讨论单个基类。 另外,引用[1]提到虚拟继承的情况,但用户问题没有涉及虚拟继承,所以我们先不展开。 示例代码(参考引用[5]中的局部对象): class E { ... }; class F { ... }; class A : public B { F f; // 其他成员 }; 在构造A的对象时,先构造基类B,然后构造成员f(按照声明顺序)。析构时,先执行A的析构函数体,然后析构成员f(即调用F的析构函数),再调用基类B的析构函数。 在引用[5]的示例中,主函数中创建了A的对象aaa,输出如下: D (因为静态成员D A::m在main之前初始化,但注意静态成员不属于对象,所以对象aaa的构造输出是:) C (静态成员C B::v初始化) E (基类B的成员e初始化) F (派生类A的成员f初始化) 析构时(main函数结束,aaa对象析构): ~F (A的成员f析构) ~E (基类B的成员e析构) ~C (静态成员在程序结束时析构,但这里输出的~C和~D是在main之后析构的,因为静态成员的生命周期是整个程序) 但是注意:对象aaa的析构只会调用非静态成员的析构函数,静态成员并不在对象析构时析构。 因此,对于用户的问题:析构函数执行时成员函数的析构顺序(注意:用户可能笔误,应该是“成员对象”的析构顺序,因为成员函数不存在析构问题) 回答:析构函数执行时,先执行自身的析构函数体,然后按成员变量声明顺序的逆序依次析构每个成员对象,最后调用基类的析构函数。 注意:这里不要混淆“成员函数”和“成员对象”。成员函数是函数,不存在析构。成员对象是类的数据成员,如果是类类型,则需要析构。 所以,我们将用户的问题理解为“成员对象”的析构顺序。 </think> 在C++中,析构函数的执行顺序遵循严格的规则,尤其涉及成员对象的析构时。以下是详细分析(基于引用内容): ### 📌 析构函数执行顺序规则 1. **执行自身析构函数体** 先执行当前类的析构函数代码块(`~ClassName()`内部语句)。 2. **析构成员对象(按声明逆序)** 按成员变量**声明顺序的逆序**依次调用各成员对象的析构函数。 🌰 若类声明为 `class A { E e; F f; }`,则析构顺序为 `~F() → ~E()`。 3. **析构基类部分** 调用直接基类的析构函数,基类再递归执行相同规则(先基类析构函数体→基类成员逆序析构→基类的基类)。 ### ️ 关键细节 1. **静态成员对象** 静态成员(如 `static D m;`)不属于对象生命周期,**不会在对象析构时被销毁**。它们在程序结束时析构(引用[5])。 2. **继承与成员对象的交互** - 构造顺序:基类→成员对象(声明顺序)→派生类自身 - 析构顺序:派生类自身→成员对象(逆序声明)→基类(引用[2][4]) 3. **初始化列表的影响** 初始化列表仅影响构造顺序,**不影响析构顺序**。析构始终按声明逆序执行(引用[4])。 4. **虚拟继承差异** 虚拟继承会调整基类构造顺序(如引用[1]所述),但**不影响成员对象的析构顺序**。 ### 📝 示例说明 ```cpp class B { E e; // 成员1 public: ~B() { /* 步骤1:执行~B()函数体 */ } // 步骤2:隐式调用 ~E()(成员逆序析构) }; class A : public B { F f; // 成员2 public: ~A() { /* 步骤1:执行~A()函数体 */ } // 步骤2:隐式调用 ~F()(成员逆序析构) // 步骤3:隐式调用基类 ~B() (触发B的析构流程) }; ``` 当 `A` 对象销毁时: 1. 执行 `~A()` 函数体 2. 析构 `F f`(声明逆序:`f` 是最后一个成员) 3. 析构基类 `B`: - 先执行 `~B()` 函数体 - 再析构 `B` 的成员 `E e` ### 💎 总结 | 阶段 | 顺序 | |--------------|-----------------------| | **构造函数** | 基类 → 成员(声明顺序)→ 自身 | | **析构函数** | 自身 → 成员(**逆序声明**)→ 基类 | > 此规则确保资源按依赖关系逆序释放,避免悬空指针(如成员依赖基类资源时,基类最后析构)[^2][^4][^5]。 --- ### ❓相关问题 1. 如果类包含多个基类(多重继承),析构顺序如何变化? 2. 虚析构函数如何影响派生类对象的析构流程? 3. 为什么静态成员对象不在类实例析构时销毁?其生命周期如何管理? 4. 在存在异常的场景下,析构函数的执行顺序是否会发生中断或改变?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ad_m1n

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值