欢迎光临散文网 会员登陆 & 注册

程序设计与算法(三)C++面向对象程序设计 北京大学 郭炜

2022-10-02 16:06 作者:siyan_Fang  | 我要投稿

《多喝水》








01~引用:int & r1=a;

“别名”

从一而终, 接下来的“=”都是赋值含义

一定要初始化

只能引用变量

常引用:const int & a;

不能通过常引用修改其引用的内容,但可通过其他方式修改被引用的内容

 exgcd(int a, int b, &x, &y)里的就是别名

02~const:

相比define有类型检查,define只是替换

常量指针 const int*p = &n

常量指针可赋值给非常量指针,反过来不行(但可强制转换)

防止自己在函数里改了不应修改的内容


03~动态内存分配:new<->delete

  1. int * P1 = new int; // 动态分配一个变量
  2. delete p1;
  3. int * P2 = new int[N]; // 一个数组
  4. // new返回值类型都是T*
  5. delete []p2; //中括号不能忘记,不然分配来的数组没有完全释放~
  6. int * P3; //不导致int类型的对象生成
  7. CRectangle * P4; //不导致对象生成, 不会调用构造函数
  8. CRectangle * P4 = new CRectangle(2,4); //调用构造函数


04~内联函数

inline int Max(int a, int b) { 函数体语句很少 }

编译时不生成函数调用语句,而是把原函数直接贴在调用位置

可执行程序的体积略微增大


04~函数重载

几个函数名称相同,使得函数命名变得简单

只要参数表不一样,就不算重复定义


04~缺省参数

void f(int x1, int x2 = 2, int x3 = 3) { }

f(10) //f(10, 2, 3)

f(10, , 8) //error

提高可扩充性 void circle(int r, int x, int y, sring color = "black")

注意避免有函数重载时的二义性(06~)

05~结构化程序设计

不足:没有封装和隐藏的概念

具体表现在,函数和其所操作的数据结构没有直观的联系


05~类和对象:带函数的结构体

封装、隐藏、抽取重用

解决~函数跟其操作的数据结构没有直观联系

  1. class CRectangle //自定义类型名 //class换成struct也没啥~
  2. {
  3. public :
  4. int w, h; //成员变量
  5. // 成员函数(不属于对象里,仅有一份)
  6. int Area(); //算面积 ,函数内容在class之外
  7. int Perimeter() { return 2*(w+h); } //算周长
  8. void Init(int _w, int _h) { w = _w; h = _h; } //初始化
  9. }; //分号~
  10. int CRectangle:: int Area() { return w*h; } //写在类之外的成员函数前要加上”类名::“,不然怎么知道对应哪个类的成员函数~
  11. int main(void)
  12. {
  13. int w, h;
  14. CRectangle r; //r是对象(类的实例),所占用的内存空间>=所有成员变量之和
  15. cin >> w >> h;
  16. r.Init(w, h);
  17. cout << r.Area() << endl << r.Perimeter();
  18. return 0;
  19. }

对象间的运算:

可赋值,不能比较,除非重载运算符


成员变量和成员函数的使用:

对象名.成员名 //上面已经用到~

指针->成员名

  1. CRectangle r1, r2;
  2. CRectangle * p1 = & r1, * p2 = & r2;
  3. p1->w = 5;
  4. p2->Init(5, 4);

引用名.成员名

  1. CRectangle & rr = r2;
  2. rr.Init(5, 4);


06~ private和public

成员变量和成员函数可分开写

int CRectangle:: int Area() { return w*h; } 

类成员的可访问范围

- private (默认) 

- public

- protected (以后再说)

私有成员变量在主函数里不能通过”类名::成员名“访问,但可通过公有成员函数修改或打印其值(视频12中有例子)


07~ 构造函数:

X::X(所有成员变量对应的参数) {每个成员变量都有了自己的值}

名字与类名相同

对对象进行初始化,自动

  1. class Complex {
  2. private :
  3. int real, imag;
  4. public :
  5. Complex(int r, int i = 0);
  6. };
  7. Complex::Complex(int r, int i) {
  8. real = r; imag = i;
  9. }
  10. Complex c1; //error
  11. Complex * pc = new Complex; //error
  12. Complex c1(2); //OK
  13. Complex c1(2, 4); //OK
  14. Complex * pc = new Complex(3, 4); //error

本文03~也中有相关内容~

08~ 复制构造函数——初始化

X::X( X & 对象)

X::X( const X & 对象)

只有一个参数,且必须是引用——即同类对象的引用

如果你没写复制构造函数,编译器会生成默认的复制构造函数(浅拷贝<或叫位拷贝>但我们一般需要深拷贝<或叫值拷贝>)

【总结】无参构造函数不一定存在,但复制构造函数一定存在


起作用的三种情况

Complex c2(c1); //当一个对象去初始化另一个对象时

上式<=> Complex c2 = c1; //这不是赋值语句,而是初始化语句

【注意】对象间赋值不导致调用复制构造函数 

void f(A a); f(a2); //对象做参数,这个形参a被a2初始化——复制构造函数

【注意】形参不一定是实参的拷贝,比如当你自己写的复制构造函数并没有赋值语句

A f(void); //对象作返回值,编译器创建临时对象,临时对象被初始化

【注意】有的编译器会进行优化,省略返回值的临时变量

这样太慢了,所以使用常量(const)引用(&)参数


09(1)~ 类型转换构造函数

Complex(一个参数) {所有成员变量都有了自己的值}


类型转换构造函数特点:

1.是构造函数 

2.只有一个参数 

3.但不是复制构造函数

比如Complex初始化需要两个参数i和r,当主函数中写道“c1 = 9”时,通过这种函数把9转化为和Complex对象格式相同的样子,视频中就是(9,0)

“类型转换构造函数”只是一类用法的名字。当需要的时候,编译器自动调用。


09(2)~ 析构函数

~Complex() {} 

没有参数

每个类仅有一个

而构造函数每个类可有多个(重载)

如果你没写析构函数,编译器会生成析构函数(不做任何操作)


10~ 构造/析构函数调用时机

(复制)构造函数:创建新对象(初始化)

类型转换构造函数:如“Complex a = 5; ”编译器将5转换成临时对象,临时对象把值给a,临时对象再被析构

析构函数:对象生命周期结束(区分:全局变量、静态局部变量、局部变量的生命周期)

【注意】使用new分配来的对象只要不delete,就不会消亡,也就是不被析构

函数参数传递对象时(形参列表或函数返回),如果不是引用,则有两个临时对象被创建,形参列表的那一个在函数调用结束时被析构,被返回的那个临时对象在调用语句结束后也被析构

备注:有的编译器为了优化,不生成返回值临时对象,就少调用一对复制构造函数和析构函数~


11~ this指针

指向成员函数所作用的对象

静态成员函数中不能使用


19~实例:实现一个CArray类(可变长数组)(就是vector)

  1. # include <iostream>
  2. # include <cstdio>
  3. using namespace std;
  4. class CArray 
  5. {
  6. int size;
  7. int cnt;
  8. int * ptr;
  9. public:
  10. CArray() //构造函数,可以用缺省参数方法,这样创建对象时如果有规定大小,写在括号里就行啦~
  11. {
  12. size = 2;
  13. cnt = 0;
  14. ptr = new int[2];
  15. printf("构造\n");
  16. }
  17. CArray(const CArray & a) //复制构造函数
  18. {
  19. size = a.size;
  20. cnt = a.cnt;
  21. //自己写复制构造函数,而不用编译器缺省出来的原因是我需要深拷贝
  22. ptr = new int[size];
  23. for (int i = 0; i < cnt; i++)
  24. {
  25. ptr[i] = a.ptr[i];
  26. }
  27. printf("复制构造\n");
  28. }

  29. void operator=(const CArray & a) //返回值最好是&,方便连等
  30. {
  31. //还要避免 a = a
  32. //如果构造函数默认size是0的话,空数组的赋值单独写
  33. //原有空间不够用时再分配新空间,效率高
  34. size = a.size;
  35. cnt = a.cnt;
  36. ptr = new int[size];
  37. for (int i = 0; i < cnt; i++)
  38. {
  39. ptr[i] = a.ptr[i];
  40. }
  41. printf("复制构造:运算符“=”重载\n");
  42. }
  43. ~CArray() //析构函数
  44. {
  45. delete[]ptr;
  46. printf("析构\n");
  47. }

  48. void push_back(int x)
  49. {
  50. if (cnt == size) //放不下了
  51. {
  52. int * oldptr = ptr;
  53. ptr = new int[size * 2];
  54. for (int i = 0; i < size; i++)
  55. {
  56. ptr[i] = oldptr[i];
  57. }
  58. delete[] oldptr;
  59. ptr[size] = x;
  60. cnt++;
  61. size *= 2;
  62. }
  63. else
  64. {
  65. ptr[cnt] = x;
  66. cnt++;
  67. }
  68. return;
  69. }

  70. int length() 
  71. {
  72. return cnt; 
  73. }

  74. int & operator [](int i) //返回类型写对啦~ (非引用的返回值不能做左值)
  75. {
  76. return ptr[i];
  77. }
  78. };
  79. int main(void)
  80. {
  81. CArray a, a2, a3;
  82. a.push_back(1); a.push_back(2); a.push_back(3);
  83. a2 = a; // “=”重载的复制构造,为了防止浅拷贝
  84. printf("遍历a2:%d %d %d\n", a2[0], a2[1], a2[2]); //重载“[]”
  85. a[1] = 5;
  86. printf("遍历a:%d %d %d\n", a[0], a[1], a[2]);
  87. CArray a4(a);//复制构造函数
  88. printf("a4现在有%d个元素\n", a4.length());
  89. printf("遍历a4:%d %d %d\n", a4[0], a4[1], a4[2]);
  90. return 0;
  91. }
  92. Output:
  93. 构造
  94. 构造
  95. 构造
  96. 复制构造:运算符“=”重载
  97. 遍历a2:1 2 3
  98. 遍历a:1 5 3
  99. 复制构造
  100. a4现在有3个元素
  101. 遍历a4:1 5 3
  102. 析构
  103. 析构
  104. 析构
  105. 析构

20~ "<<"和">>"的重载

cout是在iostream中定义的ostream类的对象

例题:

  1. # include <iostream>
  2. using namespace std;
  3. class Complex
  4. {
  5. public:
  6. int real, imag;
  7. };
  8. ostream & operator << (ostream & o, const Complex & c)
  9. {
  10. o << c.real << "+" << c.imag << "i";
  11. return o;
  12. }
  13. istream & operator >> (istream & i, Complex & c)
  14. {
  15. scanf_s("%d+%di", &c.real, &c.imag);
  16. return i;
  17. }
  18. int main(void)
  19. {
  20. Complex c1, c2;
  21. cin >> c1 >> c2;
  22. cout << c1 << endl << c2 << endl;
  23. return 0;
  24. }

弹幕中的问题:既然ostream类已经不能再加入新的成员函数,为什么不能把运算符重载函数写为Complex类的成员函数,而一定要将其写成全局函数?

把"<<"重载为Complex类的成员函数只需要1个参数("<<"的目数减1),那就得写成下面这个样子:

ostream & operator << (ostream & o)

{

o << real << "+" << imag << "i";

return o;

}

但是:

对双目运算符而言,成员运算符重载函数的形参表中仅有一个参数,它作为运算符的右操作数。另一个操作数(左操作数)是隐含的,是该类的当前对象,它是通过 this 指针隐含地递给函数的。(视频16的08:30——”a运算符b“等价于”a.operator运算符b“)

所以只有1个参数的形参表没法写,所以一定要将"<<"重载函数写成全局函数~


21~ 类型转换运算符的重载

opreator double() {return real;} //不写返回类型

cout << (double)c; //可显式转换

double n = 2 + c; //可隐式转换


22~ "++" "--" 的重载

为了区分前置和后置,前置运算符视为一元,后置运算符视为二元(第二个形参无意义随意写,但要有)

21、22综合例子:

  1. # include <iostream>
  2. using namespace std;
  3. class CDemo
  4. {
  5. private:
  6. int n;
  7. public:
  8. CDemo(int _n) { n = _n; }
  9. operator int() { return n; }
  10. friend CDemo & operator++ (CDemo & d);
  11. friend CDemo & operator--(CDemo & d);
  12. friend CDemo operator++ (CDemo & d, int);
  13. friend CDemo operator-- (CDemo & d, int);
  14. ~CDemo() {}
  15. };
  16. CDemo & operator++ (CDemo & d) { d.n++; return d; }//前置加加
  17. CDemo & operator-- (CDemo & d) { d.n--; return d; }
  18. CDemo operator++ (CDemo & d, int) { CDemo tmp(d); d.n++; return tmp; }//后置加加 //CDemo tmp(d); 这一句,使用了编译器缺省的复制构造函数
  19. CDemo operator-- (CDemo & d, int) { CDemo tmp(d.n); d.n--; return tmp; }
  20. int main(void)
  21. {
  22. CDemo d(5);
  23. cout << (d++) << endl;
  24. cout << d << endl;
  25. cout << (++d) << endl;
  26. cout << d << endl; //5677

  27. cout << (d--) << endl;
  28. cout << d << endl;
  29. cout << (--d) << endl;
  30. cout << d << endl; //5677
  31. return 0;
  32. }

【注意】++i和i++在返回值和效率上的差别:

++i:返回是i的引用,且效率高

i++:返回值是临时变量,且效率低


【运算符重载的总结】c++不能定义新的运算符、重载要符合日常习惯、重载不改变运算符优先级、有些运算符不能被重载(点号、点星、两冒号、问号冒号、sizeof)、有些运算符必须声明为类的成员函数(小括号、中括号、箭号、赋值等号)

程序设计与算法(三)C++面向对象程序设计 北京大学 郭炜的评论 (共 条)

分享到微博请遵守国家法律