引用&指针和引用&const的关系
2017-07-08 21:11
344 查看
一、引用的概念:
引用不是定义一个变量,而是给一个已经定义的变量重新起一个别名。
二、引用的定义格式:
类型&引用变量名=已定义过的变量名。
如:int b = 10; int& a = b;//a是b的别名
三、引用的特点:
1)一个变量可用多个别名;
2)引用必须初始化;
3)引用只能在初始化的时候初引用一次,不能改变为再引用其他的变量。
【例】
运行结果:
释:a、b、d的值一样,地址也一样,说明他们是一个数和一个变量可有多个别名。
b和c既不是a的拷贝,也不是指向a的指针,其实b和c就是a自己。
四、const的概念:
const 是constant的缩写,是“恒定不变”的意思。被const修饰的东西都受到C++/C语言实现的静态类型安全检查机制的保护,可以预防意外修改,能提高程序的健壮性。
五、const引用
运行结果:
六、函数参数的三种传递方式
1、值传递:如果形参为非引用的传值方式,则生成局部临时变量接收实参的值。
【例】
释:由于Func1函数体内的x是外部变量的一份拷贝,改变x的值不会影响n,所以n的值仍然是0。
2、引用传递:如果形参为引用类型,则形参是实参的别名。
【例】
释:由于Func2函数体内的x是外部变量n的引用,x和n是同一个数,x改变n也跟着变化,所以n = 10;
3、指针传递:
【例】
释:由于Func3函数体内的x是指向外部变量n的指针,改变该指针指向的内存单元的内容将导致n的值改变,所以n的值为10。
对比上述几个实例程序,你会发现:“引用传递”的性质像“指针传递”,而“值传递”像书写方式。实际上“引用”可以做的任何事情,“指针”都能够做。那么,为何还要用引用?
答案是:因为指针能够毫无约束的操作内存中的任何东西,尽管功能强大,但是非常危险。如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖个章就行了,但如果把取公章的钥匙给他,那么他就获得了不该有的权力。引用体现了最小特权原则,即给予程序元素足以完成其任务的最小权限。正如:“用合适恰当的工具做恰如其分的工作”。
//值传递
void swap1(int left, int right)
{
int tmp = left;
left = right;
right = tmp;
}//不交换
//引用传递
void swap2(int& left, int& right)
{
int tmp = left;
left = right;
right = tmp;
}//交换
//指针传递
void swap3(int *left, int *right)
{
int tmp = *left;
*left = *right;
*right = tmp;
}//交换
同上所述。
七、返回函数值
1、传值作函数返回值&传引用作函数返回值
【例】
运行结果:
2、通过汇编看函数栈帧调用
【例】
如图1:
图1 函数栈帧调用
3、通过汇编看函数值返回和函数引用返回,如图2:
图2 函数值返回和函数引用返回图
从中可知:
1)不要返回一个临时变量的引用。
2)如果返回对象出了当前函数的作用函数域依旧存在,则最好使用引用返回,因为这样更高效。
八、指针和引用的对比
1、引用在创建的同时必须初始化,即引用到一个有效地对象;而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值。
2、不存在空引用,引用必须与合法的存储单元关联,而指针可以为NULL。
【例】const int& rlin = 0;
其语义并非是把引用初始化为NULL,而是创建一个临时的int对象并用0来初始化它,然后在用它来初始化引用rlin,该临时对象将一直保留到rlin销毁的时候才会销毁。因此,不要用字面常量初始化引用。
3、引用一旦初始化指向一个对象,它就不能被改变为对另一个对象的引用(即“从一而终”),但是可以被赋值,给引用赋值并不是改变它和原始对象的绑定关系;而指针可以改变为指向另一个对象。
4、引用的创建和销毁并不会调用类的拷贝构造函数和析构函数。
5、在语言层面,引用的用法和对象一样,在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮助我们完成转换。
引用不是定义一个变量,而是给一个已经定义的变量重新起一个别名。
二、引用的定义格式:
类型&引用变量名=已定义过的变量名。
如:int b = 10; int& a = b;//a是b的别名
三、引用的特点:
1)一个变量可用多个别名;
2)引用必须初始化;
3)引用只能在初始化的时候初引用一次,不能改变为再引用其他的变量。
【例】
#define _CRT_SECURE_NO_WARNINGS 1 #include<iostream> using namespace std; int main() { int a = 10; int q = 100; int& b = a; int& c = a; //int& d;//error,引用必须要初始化 int& d = b; cout << a << ' ' << b <<" "<< d << endl; cout << &a << endl; cout << &b << endl; cout << &d << endl; system("pause"); return 0; }
运行结果:
释:a、b、d的值一样,地址也一样,说明他们是一个数和一个变量可有多个别名。
b和c既不是a的拷贝,也不是指向a的指针,其实b和c就是a自己。
四、const的概念:
const 是constant的缩写,是“恒定不变”的意思。被const修饰的东西都受到C++/C语言实现的静态类型安全检查机制的保护,可以预防意外修改,能提高程序的健壮性。
五、const引用
int main() { int d1 = 4; int& d2 = d1; d1 = 5;//d1改变,d2也会改变 d2 = 6;//d2改变,d1也会改变 int d3 = 40; const int& d4 = d3; d3 = 50;//d3改变,d4也会改变 //d4 = 60;// 4000 error,不能给常量赋值 const int d5 = 10; const int& d6 = d5; //int& d7 = d5;//error const int& d8 = 5;//常量具有常性,只有常引用可以引用常量 //d9是double类型,d10是int类型,d9赋值给d10时要生成一个临时变量,也就是说d10引用的是这个带有常性的临时变量,所以不能赋值。 double d9 = 1.1; //int& d10 = d9;//error, const int& d11 = d9; double d12 = d9; cout << d1 << " " << d2 << endl; cout << d3 << " " << d4 << endl; cout << d5 << " " << d6 << endl; cout << d8 << endl; cout << d9 << " " << d11 << " " << d12 <<endl; system("pause"); return 0; }
运行结果:
六、函数参数的三种传递方式
1、值传递:如果形参为非引用的传值方式,则生成局部临时变量接收实参的值。
【例】
void Func1(int x) { x = x + 10;//修改的是n在堆栈中的拷贝x } ... int n = 0; Func1(n); cout<< “n = ”<< n << endl; // n = 0
释:由于Func1函数体内的x是外部变量的一份拷贝,改变x的值不会影响n,所以n的值仍然是0。
2、引用传递:如果形参为引用类型,则形参是实参的别名。
【例】
void Func2(int& x) { x = x + 10;//修改的是x引用的对象n } ... int n = 0; Func2(n); cout<< “n = ”<< n << endl; // n = 10
释:由于Func2函数体内的x是外部变量n的引用,x和n是同一个数,x改变n也跟着变化,所以n = 10;
3、指针传递:
【例】
void Func3(int *x) { (*x) = (*x) + 10;//修改的是x指向的内存单元的值 } ... int n = 0; Func3(&n); cout<< “n = ”<< n << endl; // n = 10
释:由于Func3函数体内的x是指向外部变量n的指针,改变该指针指向的内存单元的内容将导致n的值改变,所以n的值为10。
对比上述几个实例程序,你会发现:“引用传递”的性质像“指针传递”,而“值传递”像书写方式。实际上“引用”可以做的任何事情,“指针”都能够做。那么,为何还要用引用?
答案是:因为指针能够毫无约束的操作内存中的任何东西,尽管功能强大,但是非常危险。如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖个章就行了,但如果把取公章的钥匙给他,那么他就获得了不该有的权力。引用体现了最小特权原则,即给予程序元素足以完成其任务的最小权限。正如:“用合适恰当的工具做恰如其分的工作”。
//值传递
void swap1(int left, int right)
{
int tmp = left;
left = right;
right = tmp;
}//不交换
//引用传递
void swap2(int& left, int& right)
{
int tmp = left;
left = right;
right = tmp;
}//交换
//指针传递
void swap3(int *left, int *right)
{
int tmp = *left;
*left = *right;
*right = tmp;
}//交换
同上所述。
七、返回函数值
1、传值作函数返回值&传引用作函数返回值
【例】
int& add1(int x, int y)//传引用作函数返回值 { int ret = 0; ret = x + y; return ret; } int add2(int x, int y)//传值作函数返回值 { int ret = 0; ret = x + y; return ret; } void test() { int a = 3, b = 4; int c = 0; int d = 0; c = add1(a, b); d = add2(a, b); cout << "c = " << c << endl; cout << "d = " << d << endl; }
运行结果:
2、通过汇编看函数栈帧调用
【例】
#include<iostream> using namespace std; int Add(int a, int b) { return a + b; } void test() { int ret = Add(1, 2); } int main() { test(); system("pause"); return 0; }
如图1:
图1 函数栈帧调用
3、通过汇编看函数值返回和函数引用返回,如图2:
图2 函数值返回和函数引用返回图
从中可知:
1)不要返回一个临时变量的引用。
2)如果返回对象出了当前函数的作用函数域依旧存在,则最好使用引用返回,因为这样更高效。
八、指针和引用的对比
1、引用在创建的同时必须初始化,即引用到一个有效地对象;而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值。
2、不存在空引用,引用必须与合法的存储单元关联,而指针可以为NULL。
【例】const int& rlin = 0;
其语义并非是把引用初始化为NULL,而是创建一个临时的int对象并用0来初始化它,然后在用它来初始化引用rlin,该临时对象将一直保留到rlin销毁的时候才会销毁。因此,不要用字面常量初始化引用。
3、引用一旦初始化指向一个对象,它就不能被改变为对另一个对象的引用(即“从一而终”),但是可以被赋值,给引用赋值并不是改变它和原始对象的绑定关系;而指针可以改变为指向另一个对象。
4、引用的创建和销毁并不会调用类的拷贝构造函数和析构函数。
5、在语言层面,引用的用法和对象一样,在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮助我们完成转换。
相关文章推荐
- C++ const限定符之顶层const & 底层const & 指针 & 引用之间的关系
- 指针&引用&const
- const T*& 指针的引用、指针的指针、const
- 指针与引用的初始化&顶层底层const变量初始化问题
- 关于指针、const、typedef、作用域::、引用&的一些理解
- C++ - const常量与指针和引用之间的关系
- 传值&传引用&传指针
- 数组的数组"、"数组的指针"、"指针的数组"和"指针的指针"的关系
- 指针剖析,地址关系 (三) 引用传递与非引用传递
- virtual +指针,引用 => 滞后联编
- C++中指针和引用的区别-转载fu_jiangtao<chinaunix>
- 指针 引用 const
- C++,常量,const,constant,引用,指针,形参,实参,函数,返回值
- & 引用形参 与 指针
- [ 转]常引用,const CString&,引用的效率
- c++数据类型--指针、字符串、const、引用
- C/C++中关于地址、指针和引用变量的学习笔记(六) : const和void
- const限定修饰符,常量指针,引用类型
- int & *p; //不能建立指向引用的指针;int *a; int * & p=a; //正确,指针变量的引用
- const 位置与指针的关系