发现程序题也挺有价值的。

顺便记录下来几道。

1.题目

  1. #include <iostream>
  2. #include <cstring>
  3. using namespace std ;
  4.  
  5. void Swap(char * const str1, char * const str2)
  6. { // 形参为锁定目标的指针(即指针常量,亦即指针的指向不能改变,内容可更改)
  7. int n = strlen(② str1 ) + ;
  8. char *temp = new char[③ n ];
  9. strcpy(temp, str1) ;
  10. strcpy(str1, str2) ;
  11. strcpy(str2, temp) ;
  12. delete [] temp;
  13. }
  14.  
  15. void Exchange(const char *&str1, const char *&str2)
  16. { // 形参不使用二级指针,而使用指针的常引用(常量指针的别名)
  17. const char *temp;
  18. temp = str1 ;
  19. str1 = str2 ;
  20. str2 = temp ;
  21. }
  22.  
  23. int main()
  24. {
  25. char str1[] = "Tom", str2[] = "Jerry";
  26. const char *p1 = "Tom", *p2 = "Jerry";
  27. cout << str1 << '\t' << str2 << endl;
  28. cout << p1 << '\t' << p2 << endl;
  29. Swap(str1, str2);
  30. Exchange(p1, p2);
  31. cout << str1 << '\t' << str2 << endl;
  32. cout << p1 << '\t' << p2 << endl;
  33. return ;
  34. }

解析:

这题应该是要搞清楚const char *p; 和 char * const p;

const char *p可以这么理解,const后面是char*,所以char*对应的内容就不能改变;而char* const p的const后面是p,所以p的值不能改变,p的值是一个指针地址。综上,紧接着const后面的内容就是不可改变的内容。这下应该可以做题了。

swap函数的const后跟的是指针,所以指针地址不能改变,而其指向地址对应的值是可以改变的,所以,我们只能想办法改变str1和str2指向的值,由于可以调用strcpy进行赋值,中间变量自然就是对应的char * const temp,这里图中没用const也可以,但我感觉要对应,还是加上const 最好。当然,我已经改过之后跑过了。我的理解是对的。

exchange函数的const后跟的是char*,这就意味着字符串的值是不能改变的,那么我们就只能交换两个别名对应的地址了,注意不要搞混,别名的绑定是不能改变的,我们只能改变别名对应的地址。临时变量也应该对应 const char* temp;这样就对了。

2.题目

  1. #include <iostream>
  2. using namespace std;
  3.  
  4. class ctest
  5. {
  6. public:
  7. ctest(int x=) : a(x) { cout << "构造对象(" << a << ")\n"; }
  8. ~ctest(){ cout << "析构对象(" << a << ")\n";}
  9. ctest & Add() // 本成员函数为引用返回
  10. {
  11. ++a;
  12. return *this;
  13. }
  14. ctest add() // 本成员函数为值返回
  15. {
  16. ctest temp(*this);
  17. ++a;
  18. return temp;
  19. }
  20. friend ostream & operator<<(ostream &out, const ctest &c)
  21. {
  22. out << c.a;
  23. return out;
  24. }
  25. private:
  26. int a;
  27. };
  28. int main()
  29. {
  30. ctest a(), b();
  31. cout << a << ", " << b << endl; // 第3行输出
  32. a.Add().Add();
  33. b.add().add(); // 拷贝构造临时无名对象时无输出
  34. cout << a << ", " << b << endl; // 第6行输出
  35. a.~ctest(); // 主动调用析构函数,并不销毁对象
  36. b.~ctest(); // 主动调用析构函数,并不销毁对象
  37. cout << a << ", " << b << endl; // 对象a,b仍可被访问。第9行输出
  38. cout << "返回操作系统。" << endl; // 第10行输出
  39. return ;
  40. }
  41. /*
  42. 构造对象(100)
  43. 构造对象(200)
  44. 100, 200
  45. 析构对象(200)
  46. 析构对象(201)
  47. 102, 201
  48. 析构对象(102)
  49. 析构对象(201)
  50. 102, 201
  51. 返回操作系统。
  52. 析构对象(201)
  53. 析构对象(102)
  54. */

解析:

这个题考输出,要注意的是无名对象的析构问题,也就是第四和第五行的输出,我们来逐行分析代码,首先创建a和b对象,分别初始化为100和200,这时候会调用两者的构造函数,因此输出两行构造函数该输出的东西。然后输出a和b的值,“100,200”,再往下执行,调用a.Add()函数,是引用返回,但是返回是其本身,期间没有创建中间变量,也就是说a.Add执行完了之后返回的结果还是a,然后后面就再执行一个a.Add,这时候返回的还是a,但是a对象的成员值自增了两次,已经变成102了,期间没有对象销毁,因此没有析构,没有输出。再往下执行,调用b.add函数,根据函数定义我们知道,函数执行过程中先拷贝构造一个temp对象,但是没有写拷贝构造函数,因此不输出任何内容,且默认是浅拷贝,然后b的成员值自增变为201,但这时候temp的成员值还是200,因为拷贝过去之后会重新分配空间,所以temp的成员值和b的成员值不是一个地址,然后返回temp,且是值返回,值返回之后其实temp还没有析构,而是作为一个无名变量继续使用,然后这个无名变量再调用add函数,导致的是temp对象的成员值+1=201,而这时候就已经和对象b无关了。但是要注意的是,由于b调用的add是值返回,b.add().add()结束之后呢,两个无名变量就没用了,就会被销毁(之前讲过,无名变量在语句结束之后被销毁,是整条语句),所以析构两个无名对象,无名对象的析构顺序又和正常对象的析构顺序刚好相反(这个我专门查了资料),所以是依次析构,因此输出的是200和201,这里唯一要理解的是,值返回的对象的析构时间并不是出了函数体,而是执行完调用这个函数的语句,才被析构。再往下走,输出a和b的成员值,a是102,b是201,然后a主动析构,输出“析构对象102”,然后b主动析构,输出“析构对象201”,注意,这里是主动析构,析构函数里面除了输出什么也没有,只是相当于调用了一个输出函数。下一步输出a、b的成员值,还是102和201,然后输出返回操作系统。下一步结束main函数,这时候才是a、b真正的析构时间,b先析构,然后是a。就很简单了。

3.题目

  1. #include <iostream>
  2. using namespace std;
  3. class Base // 基类
  4. {
  5. public:
  6. Base(int x=) : a(x)
  7. {
  8. cout << "构造基类对象(" << a << ")\n";
  9. }
  10. Base(const Base &b) : a(b.a)
  11. {
  12. cout << "拷贝构造基类的对象(" << a << ")\n";
  13. }
  14. virtual ~Base()
  15. {
  16. cout << "析构基类对象(" << a << ")\n";
  17. }
  18. protected:
  19. int a;
  20. static int num; // 静态数据成员
  21. };
  22. int Base::num = ; // 静态数据成员定义及初始化
  23. class Derived : public Base // 派生类
  24. {
  25. public:
  26. Derived(int x=, int y=) : Base(x), b(y)
  27. { // 构造函数中输出了对象个数[num],在园括号之前
  28. cout << "构造派生类的对象["
  29. << ++num << "](" << a << ", " << b << ")\n";
  30. }
  31. Derived(const Derived &d) : Base(d), b(d.b)
  32. { // 拷贝构造函数中输出了对象个数[num],在园括号之前
  33. cout << "拷贝构造派生类的对象["
  34. << ++num << "](" << a << ", " << b << ")\n";
  35. }
  36. ~Derived() // 析构函数中输出了对象个数[num],在园括号之后
  37. {
  38. cout << "析构派生类的对象("
  39. << a << ", " << b << ")[" << --num << "]\n";
  40. }
  41. void Set(int x, int y)
  42. {
  43. a = x; b = y;
  44. }
  45. friend ostream &operator<<(ostream &out, const Derived &d)
  46. {
  47. out << '(' << d.a << ", " << d.b << ')';
  48. return out;
  49. }
  50. private:
  51. int b;
  52. };
  53. void f(Derived &r, const Derived *p, Derived x)
  54. { // 形参分别为引用传递、指针传递、值传递
  55. cout << "in f function..." << endl;
  56. cout << r << endl;
  57. r.Set(, ); // 引用传递的对象,值被重置
  58. cout << *p << endl; // 虽然从指针p看其目标为常对象,但是……
  59. cout << x << endl;
  60. x.Set(, );
  61. cout << x << endl;
  62. }
  63. int main()
  64. {
  65. Derived d(, );
  66. cout << "Calling f function..." << endl;
  67. f(d, &d, d); // 请注意:用同一个对象或对象的地址值做实参
  68. cout << "return to Operating System." << endl;
  69. return ;
  70. }
  71.  
  72. /*
  73. 构造基类对象(100)
  74. 构造派生类的对象[1](100, 200)
  75. Calling f function...
  76. 拷贝构造基类的对象(100)
  77. 拷贝构造派生类的对象[2](100, 200)
  78. in f function...
  79. (100, 200)
  80. (50, 80)
  81. (100, 200)
  82. (0, 0)
  83. 析构派生类的对象(0, 0)[1]
  84. 析构基类对象(0)
  85. return to Operating System.
  86. 析构派生类的对象(50, 80)[0]
  87. 析构基类对象(50)
  88. */

解析:

还是至上而下分析,先初始化一个派生类对象d,赋值100,200,这时候先执行基类的构造函数,输出“构造基类对象100”,然后执行派生类构造函数,输出“构造派生类对象[1](100,200)”,然后进入函数,输出第三行,进入函数的过程中,第一个参数是引用传递,不构建对象,第二个参数是值传递的地址,也没有构造对象,因此这两者都不会调用构造函数,且两者指向的是同一对象;而第三个参数是值传递,传递的是对象,因此会产生实参是临时对象,会为实参构建对象,实参调用构造函数,分别输出第四行和第五行。接下来输出第六行,然后输出r的值,(100,200),然后r调用set函数重新赋值,输出p成员的值,刚才说过,r和p指向同一对象,虽然通过p不能改变其成员的值,但是通过r可以改,因此输出set之后的值(50,80)。接下来输出x,x是个临时对象,其值是构造时候的(100,200),输出。然后x调用其set函数,将临时对象的成员值都设为 了0。输出x,当然是两个0了。出函数,临时变量该析构了。按照析构的顺序先析构派生类,再析构基类。输出两行析构。接着往下走,cout一行英文,出main函数,该析构对象d了,输出两行析构,结束。

就三道啊,写不动了,让我休息会。

[学习笔记] C++ 历年试题解析(二)--程序题的更多相关文章

  1. [学习笔记] C++ 历年试题解析(一)--判断题

    少说话.. 程序题链接:https://www.cnblogs.com/aoru45/p/9898691.html 14级试题---选择题 1. 引用在声明时必须对其初始化,以绑定某个已经存在的变量( ...

  2. [学习笔记] C++ 历年试题解析(三)--小补充

    小小的补充一下吧,因为李老师又把直招的卷子发出来了.. 题目 1.有指针变量定义及初始化int *p=new int[10];执行delete [] p;操作将结束指针变量p的生命期.(×) 解释:试 ...

  3. Qlik Sense学习笔记之Mashup开发(二)

    date: 2019-01-26 11:28:07 updated: 2019-01-26 11:28:07 Qlik Sense学习笔记之Mashup开发(二) 1.Mobile SPA UI Fr ...

  4. 【视频编解码·学习笔记】11. 提取SPS信息程序

    一.准备工作: 回到之前SimpleH264Analyzer程序,找到SPS信息,并对其做解析 调整项目目录结构: 修改Global.h文件中代码,添加新数据类型UINT16,之前编写的工程中,UIN ...

  5. Android学习笔记之JSON数据解析

    转载:Android学习笔记44:JSON数据解析 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,为Web应用开发提供了一种 ...

  6. 多线程编程学习笔记——async和await(二)

    接上文 多线程编程学习笔记——async和await(一) 三.   对连续的异步任务使用await操作符 本示例学习如何阅读有多个await方法方法时,程序的实际流程是怎么样的,理解await的异步 ...

  7. 【视频编解码·学习笔记】13. 提取PPS信息程序

    PPS结构解析 与之前解析SPS方式类似 一.定义PPS类: 在3.NAL Unit目录下,新建PicParamSet.cpp和PicParamSet.h,在这两个文件中写入类的定义和函数实现. 类定 ...

  8. Javascript学习笔记三——操作DOM(二)

    Javascript学习笔记 在我的上一个博客讲了对于DOM的基本操作内容,这篇继续巩固一下对于DOM的更新,插入和删除的操作. 对于HTML解析的DOM树来说,我们肯定会时不时对其进行一些更改,在原 ...

  9. Deep Learning(深度学习)学习笔记整理系列之(二)

    Deep Learning(深度学习)学习笔记整理系列 zouxy09@qq.com http://blog.csdn.net/zouxy09 作者:Zouxy version 1.0 2013-04 ...

随机推荐

  1. 2. DVWA亲测文件包含漏洞

    Low级:     我们分别点击这几个file.php文件 仅仅是配置参数的变化: http://127.0.0.1/DVWA/vulnerabilities/fi/?page=file3.php 如 ...

  2. [翻译]Nativescript 中 Web 视图与 Android/IOS 的双向通信

    English document From http://shripalsoni.com/blog/nativescript-webview-native-bi-directional-communi ...

  3. HDU - 2571 命运 DP倍数跳跃处理

    命运 穿过幽谷意味着离大魔王lemon已经无限接近了! 可谁能想到,yifenfei在斩杀了一些虾兵蟹将后,却再次面临命运大迷宫的考验,这是魔王lemon设下的又一个机关.要知道,不论何人,若在迷宫中 ...

  4. > 软件编程 > 安卓开发 > Unity编译时找不到AndroidSDK的问题:Unable to list target pla

    http://www.qingpingshan.com/rjbc/az/228769.html 现象 在用 Unity 编译 Android 平台的应用时,遇到 Unable to list targ ...

  5. 截取HTML中的JSON数据并利用GSON进行解析(Android)

    截取HTML中的JSON数据并利用GSON进行解析(Android) 前言 最近在做的一个Android项目,需要自行搭建服务器,队友选择买了阿里云的服务器ESC产品,在数据获取上,我们采用了Andr ...

  6. Nacos深入浅出(三)

    EventDispatcher.fireEvent(new ConfigDataChangeEvent(true, dataId, group, tenant, time.getTime())); 跟 ...

  7. jqeury实现全选和反选

    注意:对于input获取属性不能用attr(),只能用prop().不然出现undefined. 第一版: <!DOCTYPE html> <html lang="en&q ...

  8. vue中params & query的比较

    共同点: 1.都可以传值 2.在另外一个组件中传递值的时候,都是放在$route中 不同点: 1.传值时候,url的表现不一样 query /orderInfo?xxx=yyy&aaa=bbb ...

  9. BZOJ 4236: JOIOJI map瞎搞

    分别记录J,O,I,的个数 cnt[char][i] 表示处理到第i位,char的个数 显然当且仅当 cnt[J][i] - cnt[O][i] == cnt[J][j-1] - cnt[O][j-1 ...

  10. 长春理工大学第十四届程序设计竞赛(重现赛)L.Homework Stream

    链接:https://ac.nowcoder.com/acm/contest/912/L 题意: 作为大珩班尖子生,小r每天有很多作业要完成,例如工图.工图和工图. 很显然,做作业是要有顺序的.作业之 ...