1 / 24

面向对象程序设计与 C++

面向对象程序设计与 C++. 第六章 引 用. 教 师: 王 涛 电 话 : 51688243 办公室 : 九教北 512 E-mail: twang@bjtu.edu.cn. 第六章 引用. 引用的定义与使用 引用参数 引用返回值 拷贝构造函数. 1 引用的定义与使用. 在同一个域中,两个对象不能有相同的名字;但通过定义引用,同一个对象可以有不同的名字 . 引用就是别名,与被引用的 “ 名字 ” 指向同一个对象。 定义引用的方法:数据类型 & 变量名; int a = 0; // 定义了一个整型变量 a

gail
Download Presentation

面向对象程序设计与 C++

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 面向对象程序设计与C++ 第六章 引 用 教 师: 王 涛 电 话: 51688243 办公室: 九教北512 E-mail: twang@bjtu.edu.cn

  2. 第六章 引用 • 引用的定义与使用 • 引用参数 • 引用返回值 • 拷贝构造函数

  3. 1 引用的定义与使用 • 在同一个域中,两个对象不能有相同的名字;但通过定义引用,同一个对象可以有不同的名字. • 引用就是别名,与被引用的“名字”指向同一个对象。 • 定义引用的方法:数据类型 &变量名; int a = 0; // 定义了一个整型变量a int *p = &a; // 定义了一个整型指针,指向a int& b = a; // 定义了一个整型引用, // b与a表示同一个变量 注:p与a是不同的对象,他们有自己不同的值,只不过可以通过*p来访问a的内容;而b和a则表示同一个对象,他们的使用完全相同。

  4. 引用的定义与使用 main(){ int a = 0; int& b = a; // 引用 printf(“\n a = %d, b = %d”, a, b); a = 10; printf(“\n a = %d, b = %d”, a, b); b = 20; printf(“\n a = %d, b = %d”, a, b); } 输出结果:a = 0, b = 0 a = 10, b = 10 a = 20, b = 20

  5. 引用的定义与使用 • 引用的定义与使用需要注意: • 引用定义的时必须初始化(必须指向已存在的对象) • 引用定义后可以修改(可以修改指向别的对象) int i = 0, j = 0; int & ri; // error,必须初始化 int & ri = i; // ok ri = &j; // error, 类型不匹配 ri = j; // ok, 定义后可以修改 int *pi; // ok, 指针可以不初始化(但不安全) pi = &i; pi = &j; // ok, 指针可以修改指向别的对象

  6. 2 引用参数 void swap1(int a, int b){ int temp = a; a = b; b = temp; } void swap2(int& a, int& b){ int temp = a; a = b; b = temp; } main(){ int a = 10, b = 20; swap1(a, b); printf(“\n a = %d, b = %d”, a, b); swap2(a, b); printf(“\n a = %d, b = %d”, a, b); } 输出结果: a = 10, b = 20 a = 20, b = 10

  7. swap1函数 void swap1(int a, int b){ int temp = a; a = b; b = temp;} swap1(a, b); a b b a 调用完swap1: …… 10 20 20 10 temp a b b a 执行完swap1: …… 10 20 10 20 10 b a swap1返回后: 20 10

  8. swap2函数 void swap2(int& a, int& b){ int temp = a; a = b; b = temp;} swap2(a, b); a b b a 调用完swap2: …… 引用 引用 20 10 temp a b b a 执行完swap2: …… 10 引用 引用 10 20 b a swap2返回后: 10 20

  9. 引用参数 • 不同类型作为函数参数: • 普通对象类型作为函数参数时,创建了一个新的对象,并将传入参数的对象数据复制给新对象。(传入的参数形式是对象) • 指针类型作为参数时,没有创建新对象,只是创建了一个指针(4字节整数),并将传入的地址复制给该指针。(传入的参数形式是对象的地址) • 引用类型作为参数时,没有创建新对象,只是创建了一个引用(别名)指向传入的参数对象.(传入的参数形式是对象) • 示例程序:06_01

  10. 引用参数 • 在使用引用参数时,为防止传入的对象被改变,可声明为const类型. class point{ int x, y; } p1; void print(point& pt){ pt.x = pt.y = 10; // 编译通过,可以修改 printf(“\n x = %d, y = %d”, pt.x, pt.y); } void print(const point& pt){ pt.x = pt.y = 10; // 编译不通过,不能修改 printf(“\n x = %d, y = %d”, pt.x, pt.y); } p1.x = p1.y = 20; print(p1);

  11. 3 缺省拷贝构造函数与‘=’ • 每定义一个类时,系统会隐式为之重载缺省的拷贝构造函数及赋值操作符‘=’,进行对象复制工作. class A{}; 相当于: class A{ A(); // 缺省构造函数 ~A(); // 缺省析构函数 A(const A&); // 缺省拷贝构造函数 void operator = (const A&); // 缺省赋值操作符 }

  12. 缺省拷贝构造函数与‘=’ • 缺省的拷贝构造函数和‘=’操作符都进行了对象的数据复制工作. class point{ public: int x,y;}; point p1, p3; p1.x = p1.y = 20; point p2(p1); // 调用缺省拷贝构造函数 p3 = p1; // 调用缺省‘=’操作符 printf(“\n p2.x = %d, p2.y = %d”, p2.x,p2.y); printf(“\n p3.x = %d, p3.y = %d”, p3.x, p3.y); 输出结果: p2.x = 20, p2.y = 20 p3.x = 20, p3.y = 20

  13. 缺省拷贝构造函数与‘=’ • 可以认为缺省拷贝构造函数与‘=’操作符函数中进行了如下操作: class point{ //… }; point::point(const point& pt){ // 复制对象所有数据 memcpy( this, &pt, sizeof(point) ); } void point::operator = (const point& pt){ // 复制对象所有数据 memcpy( this, &pt, sizeof(point) ); }

  14. 拷贝构造函数与‘=’ • 只有在用‘=’进行对象赋值时,才会调用重载的‘=’操作符函数. • 在如下情况创建对象时,调用拷贝构造函数: • 用一个类对象显示地初始化另一个对 A newA(oldA); • 把一个类对象作为实参传递给函数 A f1(A a); …… f1(oldA); • 把一个类对象作为函数返回值传递 A f1(){ A a; ……. return a; } • 容器类(vector,list等)的一些操作

  15. 缺省拷贝构造函数和‘=’的缺陷 • 一般情况下,使用缺省拷贝构造函数和‘=’操作符能够满足应用要求。 • 但若class或struct中包含非静态指针成员时,使用缺省拷贝构造函数及‘=’操作符经常会导致错误. • 此时需要程序员显式重载拷贝构造函数和‘=’操作符,显示重载后,系统改调用显式重载的拷贝构造函数和‘=’操作符。

  16. 一次调用PrintMatrix: m.elems a.elems 0,0,… 返回,删除m(delete[] m.elems): a.elems 内存被释放 自此,a.elems为野指针! 二次调用PrintMatrix: m.elems a.elems 内存被释放 返回,删除m(delete[] m.elems): Delete不是该程序的内存,出错! 缺省拷贝构造函数和‘=’的缺陷 • 示例程序:06_02

  17. 一次调用PrintMatrix: m.elems a.elems 0,0,… 0,0,… 返回,删除m(delete[] m.elems): a.elems 0,0,… 二次调用PrintMatrix: m.elems a.elems 1,2,… 1,2,… 返回,删除m(delete[] m.elems): a.elems 1,2,… 显示重载 • 显式重载拷贝构造函数后:

  18. 4 引用返回值 • 除了可以将函数参数定义为引用类型外,还可以将引用类型作为函数返回值类型。 int max(int a, int b){ return (a>b)?a:b; } int& max(int& a, int& b){ return (a>b)?a:b; }

  19. 函数返回作了什么? • 普通非引用类型返回值的函数,系统要为每个返回值创建一个临时对象。 • 使用引用类型作为返回值,则系统不需为返回值创建新对象。 • 示例程序:06_03

  20. 左值问题 • 普通非引用类型的函数返回值不能作为左值, 而引用类型的函数返回值可以作为左值使用。 int max1(int a, int b){ return a>b?a:b; } int& max2(int& a, int&b){ return a>b?a:b; } int a = 10, b = 20; max1(a, b) = 0; // error C2106: '=' : left operand must be l-value max2(a, b) = 0; // ok. 之后b = 0

  21. 警惕: 返回局部变量的引用 • 函数返回局部变量的引用,编译不会出错,但容易导致程序逻辑错误。 int& max(int a, int b){ int value = a; // value局部变量 if( b > a) value = b; return value; // 函数返回后该变量已释放 } • 示例程序:06_04

  22. 返回堆内存的引用 • 函数也可以返回堆内存的引用,但在使用完毕之后需释放内存,避免内存泄漏。(建议不要使用) set& operator * (set& set1, set& set2){ set* res = new set; //… return *res; // 返回引用 } • 示例程序:06_05

  23. 5 引用 vs 指针 • 很多编译器中,引用是通过指针来实现的。 • 很多情况下,引用和指针可以互换: • 引用参数/指针参数 • 引用返回值/指针返回值 • 引用比指针相对安全:定义引用时必须初始化;定义指针时可以不初始化。 • 使用指针参数,一般需要检查传入参数是否为空;而引用参数不必担心。 • 指针比引用相对灵活:引用初始化之后不能更改;指针初始化后可以修改指向别的地址。 • 可以用指针来实现链表;引用不行。

  24. 引用使用小结 • 尽量使用引用参数代替指针参数和实参(除非参数传入的地址会改变指向另外的对象,此时用指针参数). • 尽量使用引用返回值,但要警惕返回栈内存。 • 如需避免引用对象的内容被修改,将引用申明为const类型。 • class或struct的成员变量也可申明为引用,但尽量不要如此申明. class A{ int& a; public: A(int value):a(value){} };

More Related