C++学习笔记之 引用

先声明一下,这里的内容大多是《C++编程思想》中的内容,我最近在学习C++,觉得里面的很多话不错,另外例子也都自己实验了一番,有些现象很有趣,希望与大家分享。

?引用(reference)(&)就像能自动地被编译器间接引用的常量型指针。

#include <iostream>
using namespace std;  

int y;
int& r = y;//当一个引用被创建的时候,它必须被初始化为一个对象。
           //你也可以这样写:
const int& q = 12;//(1)这时,引用就被绑定到一个存储位置中。
int x=0;  //(2)
int& a =x;//(3)

int main()
{
    cout<<"x = "<<x<<",a = "<<a<<endl;
    a++;
    cout<<"x = "<<x<<",a = "<<a<<endl;

    system("pause");
    return 0;
}  

代码运行结果:

在位置(1),编译器分配了一个存储单元,它的初始值被初始化为12,于是这个引用就和这个存储单元联系上了。应用要点是任何引用必须和存储单元联系。访问引用时,就是访问这个存储单元。因此,增加a事实上就是增加x,这个可在main()函数中显示出来,思考一个引用最简单的方法就是把它当做一个奇特的指针,这个指针的一个优点是不必怀疑它是否被初始化了(编译器强迫它初始化),也不必知道怎样对它间接引用(这由编译器做)。

使用引用时有一定的规则:

1.当引用被创建时,它必须被初始化(指针则可以在任何时候被初始化)。

2.一旦一个引用被初始化为指向一个对象,它就不能改变为另一个对象的引用(指针则可以在任何时候指向另一个对象)。

3.不可能有NULL引用。必须确保引用是和一块合法的存储单元关联。

?函数中的引用:

最经常看见引用的地方是在函数的参数和返回值中。当引用被用作函数参数时,在函数内任何对引用的更改将对函数外的参数产生改变。当然,可以通过传递一个指针来做相同的事情,但引用具有更清晰的语法。

如果从函数中返回一个引用,必须像从函数中返回一个指针一样对待,当函数返回时,无论引用关联的是什么都应该存在,否则,将不知道指向哪一个内存。

int& h()
{
    int q;
    return q;//警告,返回的是局部变量或临时变量的地址
}

出现结果:

int& h()
{
    static int x;
    return x;//OK
}

这样就没有报错。

#include <iostream>
using namespace std;  

int* f(int* x)
{
    (*x)++;
    return x;
}

int& g(int& x)
{
    x++;
    return x;
}

int main()
{
    int a=0;

    cout<<"f(&a) = "<<f(&a)<<endl;
    cout<<"a = "<<a<<endl;

    cout<<"g(a) = "<<g(a)<<endl;
    cout<<"a = "<<a<<endl;

    system("pause");
    return 0;
}  

可见f()函数传进去的是a的地址,返回的也是a的地址。

g()函数传进去的是a的引用,相当于传进去的a的存储单元的值,返回的是a的存储单元的值。

对函数f()的调用缺乏使用引用的方便性和清晰性,但很清楚这是传递一个地址。在函数g()的调用中,地址通过引用被传递,但表面上看不出来。

?常量引用

刚刚函数中参数是非常量对象时,这个引用参数才能工作。如果是常量对象,函数g()将不接受这个参数,这样做是一件好事,因为这个函数将改变外部参数。如果知道这函数不妨碍对象的不变性的话,让这个参数是一个常量引用将允许这个函数在任何情况下使用。这意味着,对于用户定义的类型,该函数只能调用常量成员函数,而且不应当改变任何公共的数据成员。

#include <iostream>
using namespace std;  

void f(int& x)
{
    x++;
}

void g(const int& x)
{
    x++;
}

int main()
{
    int a=0;

    f(1);
    g(1);

    system("pause");
    return 0;
}

调用f(1)会产生编译期间错误,这是因为编译器必须首先建立一个引用,即编译器为一个int类型分派存储单元,同时将其初始化为1并为其产生一个地址和引用捆绑在一起。存储的内容必须是常量,因为改变它没有任何意义,我们再不能对它进行操作。当改变这种数据的时候,编译器会指出错误,这是非常有用的提示,因为这个改变会导致信息丢失。

?指针引用

在C语言中,如果想改变指针本身而不是它所指向的内容,函数声明可能像这样:

void f(int **)

当传递它时,必须取得指针的地址:

int i = 47;
int* ip = &i;
f(&ip);

对于C++中的引用,语法清晰多了。函数参数变成指针的引用,用不着取得指针的地址。

int*& i理解的话,相比于int& i。我的理解是int*& i, i首先是一个引用,之后是对int*的引用,即指针的引用。

#include <iostream>
using namespace std;  

void increment(int*& i)
{
    i++;
}

int main()
{
    int* i=0;

    cout<<"i = "<<i<<endl;
    increment(i);
    cout<<"i = "<<i<<endl;

    system("pause");
    return 0;
}  

结果很有意思,定义i是一个指针,指向的地址是0。执行increment(i)函数之后,指针加一,因为是整型变量的指针,指向下一个整型变量,内存地址就加了4。所以结果是:

有什么说错的地方,还望大家多多指正。

时间: 2024-08-26 10:18:18

C++学习笔记之 引用的相关文章

C语言学习笔记(2):引用头文件所使用的符号区别

C语言引用头文件使用的符号有两种,以头文件stdio.h为例: (1)#include <stdio.h> (2)#include “stdio.h” 这两种引用方式是存在不同的. 第一种引用方式,编译器仅在标准库头文件中进行匹配: 第二种引用方式,编译器先在工程目录下进行匹配,如果没有,再到标准库头文件中查找. 这里进行了测试,应用VC++6.0,新建文件,首先是主文件Test.c: #include <child.h> void main() { Test(); } 然后,为了

Python学习笔记 | 变量 + 引用 + 拷贝 + 作用域

在Python中,变量是没有类型的,在使用变量的时候,不需要提前声明,只需要给这个变量赋值即可.但是,当用变量的时候,必须要给这个变量赋值:如果只写一个变量,而没有赋值,那么Python认为这个变量没有定义(not defined). 一.变量和对象 1. 可变对象和不可变对象 在Python中,对象分为两种:可变对象和不可变对象,不可变对象包括int,float,long,str,tuple等,可变对象包括list,set,dict等.需要注意的是:这里说的不可变指的是值的不可变.对于不可变类

C++学习笔记2——引用

1. int ival = 1; int &refVal = ival; //引用必须初始化,且类型严格匹配 2. int ival = 1; int &refVal = ival; int const *refval = &ival;//两者等价,即引用所占的内存空间和指针大小相同 3.引用不能引用自身 4.可以对一个变量多次应用,即起多个别名 5.函数的返回值为引用   给变量赋值 给引用赋值 当左值 栈变量 可以 有可能出错,返回的是局部变量的地址 静态变量.全局变量 可以

Java学习笔记-方法引用

方法引用(Method Reference) 上一篇中记录了Lambda表达式,其可以创建匿名方法.当Lambda表达式只是调用一个存在的方法时,可以采用方法引用(JDK8具有的特性).如下: 1 public class Person { 2 3 public enum Sex { 4 MALE, FEMALE 5 } 6 7 String name; 8 LocalDate birthday; 9 Sex gender; 10 String emailAddress; 11 int age;

HTML学习笔记 iframe引用页面按列(针对target打开方式解释) 第九节 (原创)

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>FrameA</title> </head> <body bgcolor="#a52a2a"> FrameA <a href="http://www.baidu.com" target=&

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员).拷贝构造函数  从概念上将,可以认为构造函数分为两个阶段执行: 1)初始化阶段: 2)普通的计算阶段.计算阶段由构造函数函数体中的所有语句组成. 一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 1.对象成员及其初始化 <span style="font-size:14px;">#include <iostream> using namespace std;

swift学习笔记(三)关于拷贝和引用

在swift提供的基本数据类型中,包括Int ,Float,Double,String,Enumeration,Structure,Dictionary都属于值拷贝类型. 闭包和函数同属引用类型 捕获则为拷贝.捕获即定义这些常量和变量的原作用域已不存在,闭包仍然可以在闭包函数体内引用和修改这些值 class属于引用类型. Array的情况稍微复杂一些,下面主要对集合类型进行分析: 一.关于Dictionary:无论何时将一个字典实例赋给一个常量,或者传递给一个函数方法时,在赋值或调用发生时,都会

Android学习笔记_78_ Android开发中使用软引用和弱引用防止内存溢出

在<Effective Java 2nd Edition>中,第6条"消除过期的对象引用"提到,虽然Java有 垃圾回收机制,但是只要是自己管理的内存,就应该警惕内存泄露的问题,例如的对象池.缓存中的过期对象都有可能引发内存泄露的问题.书中还提到可以用 WeakHashMap来作为缓存的容器可以有效解决这一问题.之前也确实遇到过类似问题,但是没有接触过"弱引用"相关的问题,于是查阅了一些资料. <Java 理论与实践: 用弱引用堵住内存泄漏>

C++学习笔记之——内联函数,引用

本文为原创作品,转载请注明出处 欢迎关注我的博客:http://blog.csdn.net/hit2015spring和http://www.cnblogs.com/xujianqing/ 作者:晨凫追风 一直想开始写C++的学习笔记,学习C++已经两个月了,今天开始写一下引用,内联函数,的一些概念和作用吧.那么开始吧! ????内联函数: 我们写的程序最终都是要用编译器,进行编译链接形成一段机器可以知道的二进制代码,接着存到一个内存中,这时候每一段程序代码都会有自己的一个地址,计算机按照地址增