C++复习(四)
来源:互联网 发布:windows xp启动不了 编辑:程序博客网 时间:2024/05/16 20:28
C++引用(Reference)
引用(Reference)是C++相对于C语言的又一个扩充。引用类似于指针,只是在声明的时候用 & 取代了 *。引用可以看做是被引用对象的一个别名,在声明引用时,必须同时对其进行初始化。引用的声明方法如下:
类型标识符 &引用名 = 被引用对象
[例1]C++引用示例:
int a = 10;
int &b = a;
cout<<a<<" "<<b<<endl;
cout<<&a<<" "<<&b<<endl;
在本例中,变量 b 就是变量 a 的引用,程序运行结果如下:
10 10
0018FDB4 0018FDB4
从运行结果可以看出,变量 a 和变量 b 都是指向同一地址的,也即变量 b 是变量 a 的另一个名字,也可以理解为 0018FDB4 空间拥有两个名字:a和b。由于引用和原始变量都是指向同一地址的,因此通过引用也可以修改原始变量中所存储的变量值,如例2所示,最终程序运行结果是输出两个20,可见原始变量a的值已经被引用变量b修改。
[例2]通过引用修改原始变量中的值:
int a = 10;
int &b = a;
b = 20;
cout<<a<<" "<<b<<endl;
如果我们不希望通过引用来改变原始变量的值时,我们可以按照如下的方式声明引用:
const 类型标识符 & 引用名 = 被引用的变量名
这种引用方式成为常引用。如例3所示,我们声明b为a的常引用,之后尝试通过b来修改a变量的值,结果编译报错。虽然常引用无法修改原始变量的值,但是我们仍然可以通过原始变量自身来修改原始变量的值,如例3中,我们用a=20;语句将a变量的值由10修改为20,这是没有语法问题的。
[例3]不能通过常引用来修改原始值:
int a = 10;
const int &b = a;
b = 20; //compile error
a = 20;
通过例2,我们可以知道通过引用我们可以修改原始变量的值,引用的这一特性使得它用于函数传递参数或函数返回值时非常有用。
1) 函数引用参数
如果我们在声明或定义函数的时候将函数的形参指定为引用,则在调用该函数时会将实参直接传递给形参,而不是将实参的拷贝传递给形参。如此一来,如果在函数体中修改了该参数,则实参的值也会被修改。这跟函数的普通传值调用还是有区别的。
[例3]函数的引用传值:
#include<iostream>
using namespace std;
void swap(int &a, int &b);
int main(){
int num1 = 10;
int num2 = 20;
cout<<num1<<" "<<num2<<endl;
swap(num1, num2);
cout<<num1<<" "<<num2<<endl;
return 0;
}
void swap(int &a, int &b){
int temp = a;
a = b;
b = temp;
}
运行结果:
10 20
20 10
在本例中我们将swap函数的形参声明为引用,在调用swap函数的时候程序是将变量num1和num2直接传递给形参的,其中a是num1的别名,b是num2的别名,在swap函数体中交换变量a和变量b的值,也就相当于直接交换变量num1和变量num2的值了,因此程序最后num1=20,num2=10。
2) 函数引用返回值
在C++中非void型函数需要返回一个返回值,类似函数形参,我们同样可以将函数的返回值声明为引用。普通的函数返回值是通过传值返回,即将关键字return后面紧接的表达式运算结果或变量拷贝到一个临时存储空间中,然后函数调用者从临时存储空间中取到函数返回值,如例4所示。
[例4]函数的普通返回值:
#include<iostream>
using namespace std;
int valplus(int &a);
int main(){
int num1 = 10;
int num2;
num2 = valplus(num1);
cout<<num1<<" "<<num2<<endl;
return 0;
}
int valplus(int &a){
a = a + 5;
return a;
}
在例4中,valplus函数采用的是普通的传值返回,也即将变量a的结果加上5之后,将结果拷贝到一个临时存储空间,然后再从临时存储空间拷贝给num2变量。 当我们将函数返回值声明为引用的形式时,如例5所示。虽然例5运行结果和例4是相同的,但例5中的valplus函数在将a变量加上5之后,其运算结果是直接拷贝给num2的,中间没有经过拷贝给临时空间,再从临时存储空间中拷贝出来的这么一个过程。这就是普通的传值返回和引用返回的区别。
[例5]函数的引用返回值:
#include<iostream>
using namespace std;
int & valplus(int &a);
int main(){
int num1 = 10;
int num2;
num2 = valplus(num1);
cout<<num1<<" "<<num2<<endl;
return 0;
}
int & valplus(int &a){
a = a + 5;
return a;
}
此外,我们还需要注意一个小问题。如果我们将例5中的valplus函数定义成例6中所示的形式,那么这段程序就会产生一个问题,变量b的作用域仅在这个valplus函数体内部,当函数调用完成,b变量就会被销毁。而此时我们若将b变量的值通过引用返回拷贝给变量num2的时候,有可能会出现在拷贝之前b变量已经被销毁,从而导致num2变量获取不到返回值。虽然这种情况在一些编译器中并没有发生,但是我们在设计程序的时候也是应该尽量避免这一点的。
在例4和例5中,我们就是为了避免这一点才采用的引用传递参数。普通的传值返回则不存在这样的问题,因为编译器会将返回值拷贝到临时存储空间后再去销毁b变量的。
[例6]一个可能获取不到返回值的例子:
复制纯文本新窗口
int & valplus(int a){
int b = a+5;
return b;
}
类型标识符 &引用名 = 被引用对象
[例1]C++引用示例:
int a = 10;
int &b = a;
cout<<a<<" "<<b<<endl;
cout<<&a<<" "<<&b<<endl;
在本例中,变量 b 就是变量 a 的引用,程序运行结果如下:
10 10
0018FDB4 0018FDB4
从运行结果可以看出,变量 a 和变量 b 都是指向同一地址的,也即变量 b 是变量 a 的另一个名字,也可以理解为 0018FDB4 空间拥有两个名字:a和b。由于引用和原始变量都是指向同一地址的,因此通过引用也可以修改原始变量中所存储的变量值,如例2所示,最终程序运行结果是输出两个20,可见原始变量a的值已经被引用变量b修改。
[例2]通过引用修改原始变量中的值:
int a = 10;
int &b = a;
b = 20;
cout<<a<<" "<<b<<endl;
如果我们不希望通过引用来改变原始变量的值时,我们可以按照如下的方式声明引用:
const 类型标识符 & 引用名 = 被引用的变量名
这种引用方式成为常引用。如例3所示,我们声明b为a的常引用,之后尝试通过b来修改a变量的值,结果编译报错。虽然常引用无法修改原始变量的值,但是我们仍然可以通过原始变量自身来修改原始变量的值,如例3中,我们用a=20;语句将a变量的值由10修改为20,这是没有语法问题的。
[例3]不能通过常引用来修改原始值:
int a = 10;
const int &b = a;
b = 20; //compile error
a = 20;
通过例2,我们可以知道通过引用我们可以修改原始变量的值,引用的这一特性使得它用于函数传递参数或函数返回值时非常有用。
1) 函数引用参数
如果我们在声明或定义函数的时候将函数的形参指定为引用,则在调用该函数时会将实参直接传递给形参,而不是将实参的拷贝传递给形参。如此一来,如果在函数体中修改了该参数,则实参的值也会被修改。这跟函数的普通传值调用还是有区别的。
[例3]函数的引用传值:
#include<iostream>
using namespace std;
void swap(int &a, int &b);
int main(){
int num1 = 10;
int num2 = 20;
cout<<num1<<" "<<num2<<endl;
swap(num1, num2);
cout<<num1<<" "<<num2<<endl;
return 0;
}
void swap(int &a, int &b){
int temp = a;
a = b;
b = temp;
}
运行结果:
10 20
20 10
在本例中我们将swap函数的形参声明为引用,在调用swap函数的时候程序是将变量num1和num2直接传递给形参的,其中a是num1的别名,b是num2的别名,在swap函数体中交换变量a和变量b的值,也就相当于直接交换变量num1和变量num2的值了,因此程序最后num1=20,num2=10。
2) 函数引用返回值
在C++中非void型函数需要返回一个返回值,类似函数形参,我们同样可以将函数的返回值声明为引用。普通的函数返回值是通过传值返回,即将关键字return后面紧接的表达式运算结果或变量拷贝到一个临时存储空间中,然后函数调用者从临时存储空间中取到函数返回值,如例4所示。
[例4]函数的普通返回值:
#include<iostream>
using namespace std;
int valplus(int &a);
int main(){
int num1 = 10;
int num2;
num2 = valplus(num1);
cout<<num1<<" "<<num2<<endl;
return 0;
}
int valplus(int &a){
a = a + 5;
return a;
}
在例4中,valplus函数采用的是普通的传值返回,也即将变量a的结果加上5之后,将结果拷贝到一个临时存储空间,然后再从临时存储空间拷贝给num2变量。 当我们将函数返回值声明为引用的形式时,如例5所示。虽然例5运行结果和例4是相同的,但例5中的valplus函数在将a变量加上5之后,其运算结果是直接拷贝给num2的,中间没有经过拷贝给临时空间,再从临时存储空间中拷贝出来的这么一个过程。这就是普通的传值返回和引用返回的区别。
[例5]函数的引用返回值:
#include<iostream>
using namespace std;
int & valplus(int &a);
int main(){
int num1 = 10;
int num2;
num2 = valplus(num1);
cout<<num1<<" "<<num2<<endl;
return 0;
}
int & valplus(int &a){
a = a + 5;
return a;
}
此外,我们还需要注意一个小问题。如果我们将例5中的valplus函数定义成例6中所示的形式,那么这段程序就会产生一个问题,变量b的作用域仅在这个valplus函数体内部,当函数调用完成,b变量就会被销毁。而此时我们若将b变量的值通过引用返回拷贝给变量num2的时候,有可能会出现在拷贝之前b变量已经被销毁,从而导致num2变量获取不到返回值。虽然这种情况在一些编译器中并没有发生,但是我们在设计程序的时候也是应该尽量避免这一点的。
在例4和例5中,我们就是为了避免这一点才采用的引用传递参数。普通的传值返回则不存在这样的问题,因为编译器会将返回值拷贝到临时存储空间后再去销毁b变量的。
[例6]一个可能获取不到返回值的例子:
复制纯文本新窗口
int & valplus(int a){
int b = a+5;
return b;
}
C/C++类的声明和对象的创建
类是创建对象的模板,一个类可以创建多个对象,每个对象都是类类型的一个变量;创建对象的过程也叫类的实例化。每个对象都是类的一个具体实例(Instance),拥有类的成员变量和成员函数。
与结构体一样,类只是一种复杂数据类型的声明,不占用内存空间。而对象是类这种数据类型的一个变量,占用内存空间。
类的声明
类是用户自定义的类型,如果程序中要用到类,必须先进行声明,或者使用已存在的类(别人写好的类、标准库中的类等),C++语法本身并不提供现成的类的名称、结构和内容。
一个简单的类的定义:
class Student{
//成员变量
char *name;
int age;
float score;
//成员函数
void say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
};
该例创建了一个Student类,它包含了3个成员变量和1个成员函数。
class是C++中的关键字,用于声明一个类;紧接 class 关键字之后的是我们自定义的类名 Student;由{ }包围的是类体。声明类时不能对成员变量进行初始化,只有在创建对象以后才能赋值。
类可以理解为一种新的数据类型,该数据类型的名称是 Student。与 char、int、float 等基本数据类型不同的是,Student 是一种复杂数据类型,可以包含基本类型,而且多了很多基本类型中没有的特性。
需要注意的是:在类声明的最后有一个分号(;),它是类声明的一部分,表示类声明结束了,不能省略。
创建对象
声明了 Student 数据类型之后,就可以用它来定义变量了,如:
Student LiLei; //创建对象
该语句声明了一个名字为 LiLei、数据类型为 Student 的变量。这和:
int a; //定义整形变量
语句定义了一个整型变量表达的意思是类似的。而 LiLei 这个变量我们称之为 Student 类的对象。
在定义类的对象时,class 关键字可要可不要。但出于习惯我们通常都会省略掉class关键字,例如:
class Student LiLei; //正确
Student LiLei; //同样正确
创建类的对象时,除了能定义单个变量以外,还可以定义一个数组或者指针。例如:
Student all_student[100];
Student *pointer;
第一条语句定义了一个 all_student 数组,该数据拥有100个元素,每个元素都是 Student 类型。第二条语句定义了一个 Student 类型的指针 pointer,该指针可以指向 Student 类型的变量(对象),用法和普通指针一样。
声明类的同时创建对象
和结构体(struct)类似,你可以先声明类,然后再创建对象,也可以在声明类的同时创建对象。如下所示:
class Student{
//成员变量
char *name;
int age;
float score;
//成员函数
void say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
}stu1, stu2;
这个时候你也可以省略类名,直接创建对象。如下所示:
class{
//成员变量
char *name;
int age;
float score;
//成员函数
void say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
}stu1, stu2;
直接定义对象,在C++中是合法的、允许的,但却很少用,也不提倡用。
一个完整的示例:
#include <stdio.h>
//类可以在所有函数之外定义
class Student{
public: //类包含的变量
char *name;
int age;
float score;
public: //类包含的函数
void say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
};
int main(){
//创建对象
Student stu;
stu.name = "小明";
stu.age = 15;
stu.score = 92.5f;
stu.say();
//定义指针
Student *pt_stu = &stu;
pt_stu->name = "李雷";
pt_stu->age = 16;
pt_stu->score = 80;
pt_stu->say();
return 0;
}
运行结果:
小明的年龄是 15,成绩是 92.500000
李雷的年龄是 16,成绩是 80.000000
public 是C++中的关键字,用来修饰成员变量和成员函数,表示它们是公有的。我们将在下节详细讲解 public,这里只需要知道,只有 public 后面的成员变量和成员函数才能被创建的对象访问到。像本节开头的例子,没有使用 public,那么创建对象后就不能使用任何成员。
main 函数中首先创建了一个对象 stu,然后又定义了一个 Student 类型的指针变量。可以发现,和结构体(struct)类似,一个对象通过成员选择符”.“来访问成员变量和成员函数,而指针变量通过指针操作符”->“来访问成员。
对象指针和结构体指针类似,不理解的同学请重温《C语言结构体和指针》。
注意:对象指针指向的是一个具体的对象,而不是类。下面的写法是错误的:
复制纯文本新窗口
Student *pt;
pt = &Student;
与结构体一样,类只是一种复杂数据类型的声明,不占用内存空间。而对象是类这种数据类型的一个变量,占用内存空间。
类的声明
类是用户自定义的类型,如果程序中要用到类,必须先进行声明,或者使用已存在的类(别人写好的类、标准库中的类等),C++语法本身并不提供现成的类的名称、结构和内容。
一个简单的类的定义:
class Student{
//成员变量
char *name;
int age;
float score;
//成员函数
void say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
};
该例创建了一个Student类,它包含了3个成员变量和1个成员函数。
class是C++中的关键字,用于声明一个类;紧接 class 关键字之后的是我们自定义的类名 Student;由{ }包围的是类体。声明类时不能对成员变量进行初始化,只有在创建对象以后才能赋值。
类可以理解为一种新的数据类型,该数据类型的名称是 Student。与 char、int、float 等基本数据类型不同的是,Student 是一种复杂数据类型,可以包含基本类型,而且多了很多基本类型中没有的特性。
需要注意的是:在类声明的最后有一个分号(;),它是类声明的一部分,表示类声明结束了,不能省略。
创建对象
声明了 Student 数据类型之后,就可以用它来定义变量了,如:
Student LiLei; //创建对象
该语句声明了一个名字为 LiLei、数据类型为 Student 的变量。这和:
int a; //定义整形变量
语句定义了一个整型变量表达的意思是类似的。而 LiLei 这个变量我们称之为 Student 类的对象。
在定义类的对象时,class 关键字可要可不要。但出于习惯我们通常都会省略掉class关键字,例如:
class Student LiLei; //正确
Student LiLei; //同样正确
创建类的对象时,除了能定义单个变量以外,还可以定义一个数组或者指针。例如:
Student all_student[100];
Student *pointer;
第一条语句定义了一个 all_student 数组,该数据拥有100个元素,每个元素都是 Student 类型。第二条语句定义了一个 Student 类型的指针 pointer,该指针可以指向 Student 类型的变量(对象),用法和普通指针一样。
声明类的同时创建对象
和结构体(struct)类似,你可以先声明类,然后再创建对象,也可以在声明类的同时创建对象。如下所示:
class Student{
//成员变量
char *name;
int age;
float score;
//成员函数
void say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
}stu1, stu2;
这个时候你也可以省略类名,直接创建对象。如下所示:
class{
//成员变量
char *name;
int age;
float score;
//成员函数
void say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
}stu1, stu2;
直接定义对象,在C++中是合法的、允许的,但却很少用,也不提倡用。
一个完整的示例:
#include <stdio.h>
//类可以在所有函数之外定义
class Student{
public: //类包含的变量
char *name;
int age;
float score;
public: //类包含的函数
void say(){
printf("%s的年龄是 %d,成绩是 %f\n", name, age, score);
}
};
int main(){
//创建对象
Student stu;
stu.name = "小明";
stu.age = 15;
stu.score = 92.5f;
stu.say();
//定义指针
Student *pt_stu = &stu;
pt_stu->name = "李雷";
pt_stu->age = 16;
pt_stu->score = 80;
pt_stu->say();
return 0;
}
运行结果:
小明的年龄是 15,成绩是 92.500000
李雷的年龄是 16,成绩是 80.000000
public 是C++中的关键字,用来修饰成员变量和成员函数,表示它们是公有的。我们将在下节详细讲解 public,这里只需要知道,只有 public 后面的成员变量和成员函数才能被创建的对象访问到。像本节开头的例子,没有使用 public,那么创建对象后就不能使用任何成员。
main 函数中首先创建了一个对象 stu,然后又定义了一个 Student 类型的指针变量。可以发现,和结构体(struct)类似,一个对象通过成员选择符”.“来访问成员变量和成员函数,而指针变量通过指针操作符”->“来访问成员。
对象指针和结构体指针类似,不理解的同学请重温《C语言结构体和指针》。
注意:对象指针指向的是一个具体的对象,而不是类。下面的写法是错误的:
复制纯文本新窗口
Student *pt;
pt = &Student;
0 0
- 【C语言复习(四)】指针基础
- C语言复习(四)
- C Primer Plus,5th Edition 复习(四)
- java复习(四)
- C++复习(四)
- 复习四:C的OOP-多层继承
- 【学习笔记四】C语言基础知识复习
- C++复习(四)小九九
- servlet复习(四)过滤器
- 四大组件复习(四)
- JAVA 复习总结(四)
- C++知识点复习(四)
- Java面试复习(四)
- Android 基础复习(四)
- 第二学期复习---(四)IO
- 操作系统复习四(存储器管理)
- 复习软考的心路历程(四)
- 【原创】国庆PE总复习(四)
- 8.Oracle深度学习笔记——BUFFER CACHE深入一
- R语言 plyr包 m_ply mdply
- HDOJ 1085 Holding Bin-Laden Captive!(母函数)(未解决)
- 【Android】Android网络开发详解
- hive安装配置与调试
- C++复习(四)
- DSP28335的一些笔记
- POJ 3728The merchant(dp+LCA+倍增)
- jvm中堆栈以及内存区域分配
- MongoDB 应用案例
- 从头开始
- 跨域
- Fedora下的简易Samba设置
- hdu 2444 The Accomodation of Students(二分图判断+匈牙利算法)