C++---指针和引用

目录

 

指针

引用

将引用用作函数参数

引用的属性和特别之处

临时变量 引用参数和const

应尽可能使用const

将引用用于结构


指针

指针持有一个对象的地址,称为指针"指向"这个对象。通过指针可以间接操纵它指向的对象,指针的典型用法包括:

  1. 构造链式的数据结构,如链表和树
  2. 管理程序运行时动态分配的对象
  3. 作为函数的参数

每个指针都有相关的类型,需要在定义指针时指出。指针的类型指出了如何解释该内存地址保存的内容,以及该内存区域应该有多大。

 

引用

引用又称为别名,它可以作为对象的另一个名字。通过引用可以间接地操作的对象。在程序中,引用主要用作函数的参数。

引用由类型标识符合一个取地址符(&)来定义,引用必须被初始化,初始值是一个有内存地址的对象,如变量。

#include 
using namespace std;
int main()
{
    int a = 10;
    int& ra = a;//ra是变量a的引用
    cout << "a=" << a << "\t" << "&a" << &a <

运行结果: 

a=10    &a0x6dfef8
ra=10   &ra0x6dfef8
a=20    &a0x6dfef8
ra=20   &ra0x6dfef8

引用一旦被初始化,就不能再指向其他的对象,对引用的所有操作都会被应用在它所指向的对象上。

#include 
using namespace std;
int main()
{
    int x = 100, y = 20;
    int &r = x; //r是x的引用
    printf("x=%d,y=%d,r=%d\n", x, y, r);
    r = y; //r不会变成y的引用,而是x=y
    printf("x=%d,y=%d,r=%d\n", x, y, r);

}

运行结果:

x=100,y=20,r=100
x=20,y=20,r=20

可以看到,虽然是将y赋值给r,但是x的值也改变了

指针和引用既有相似之处,又有很多不同:

(1)定义和初始化

指针的定义形式如下:

类型 *指针变量;

指针保存指定类型的对象的地址,一个指针可以指向同类型的不同对象。eg:

    int x = 10, y = 20;
    int *pi; //可以不初始化
    pi = &x; //p可以指向X
    pi = &y; //p也可以重新指向y

引用的定义形式为:

类型 &引用名 = 初始值;

引用时一个对象的别名,定义引用时必须用有内存地址的对象初始化。引用在初始化之后,一直指向该对象。eg:


    int a = 10, b = 20;
    int &ri = a;//必须初始化,ri是a的引用,是a的别名
    ri = b; //对ri的操作就是对a的操作,等同于a=b

(2)使用方式

指针通过解引用(*)运算间接访问指向的对象;引用作为对象的别名,可以直接访问对象。

(3)指针可以不指向任何对象,其值为0,表示空指针。引用必须指向一个对象,而且一直指向该对象,不存在"空"引用。引用的值如果是0,表示它指向的单元值是0

(4)指针之间的相互赋值会改变指向关系;引用之间的相互赋值是它们指向的对象之间的赋值,引用关系本身并不改变。

将引用用作函数参数

/* swaps.cpp--swapping with references and with pointers */
#include 

using namespace std;
void swapr(int &a, int &b); //a,b are aliases for ints
void swapp(int *p, int *q); //p,q are addresses of ints
void swapv(int a, int b); //a,b are new variables
int main()
{
    int wallet1 = 300;
    int wallet2 = 350;
    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

    cout << "Using references to swap contents:\n";
    swapr(wallet1, wallet2);
    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

    cout << "Using pointers to swap contents again:\n";
    swapp(&wallet1, &wallet2);
    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

    cout << "Trying to use passing by value:\n";
    swapv(wallet1, wallet2);
    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

    return 0;
}

void swapr(int &a, int &b) //use references
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}

void swapp(int *p, int *q)
{
    int temp;
    temp = *p;
    *p = *q;
    *q = temp;
}

void swapv(int a, int b)
{
    int temp;
    temp = a;
    a = b;
    b = temp;
}
wallet1 = $300 wallet2 = $350
Using references to swap contents:
wallet1 = $350 wallet2 = $300
Using pointers to swap contents again:
wallet1 = $300 wallet2 = $350
Trying to use passing by value:
wallet1 = $300 wallet2 = $350

引用的属性和特别之处

/* cubes.cpp--regular and reference arguments */
#include 

using namespace std;
double cube(double a);
double refcube(double &ra);
int main()
{
    double x = 3.0;
    cout << cube(x);
    cout << " = cube of " << x << endl;

    cout << refcube(x);
    cout << " = cube of " << x << endl;
    return 0;

}

double cube(double a)
{
    a *= a * a;
    return a;
}

double refcube(double &ra)
{
    ra *= ra * ra;
    return ra;
}
27 = cube of 3
27 = cube of 27

可以看到,refcube函数修改了main中的x值,而cube没有,这提醒我们为何通常按值传递。变量a位于cube中,它被初始化为x的值,但修改a并不会影响x。

但由于refcube使用了引用参数,因此修改ra实际上就是修改x。 如果程序员的意图是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应使用常量引用。

例如,在这个例子中,应在函数原型和函数头中使用const:

double refcube(const double &ra);

如果这样做,当编译器发现代码修改了ra的值时,将生成错误信息。

||=== Build: Debug in firstref (compiler: GNU GCC Compiler) ===|
E:\codeblock_code\c++\reference\firstref\main.cpp||In function 'double refcube(const double&)':|
E:\codeblock_code\c++\reference\firstref\main.cpp|27|error: assignment of read-only reference 'ra'|
||=== Build failed: 1 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

临时变量 引用参数和const

如果实参与引用参数不匹配,c++将生成临时变量。当前,仅当参数为const引用时,C++才允许这样做。

如果引用参数是const,则编译器将在下面两种情况下生成临时变量:

  • 实参的类型正确,但不是左值
  • 实参的类型不正确,但可以转换为正确的类型

左值是什么呢?左值参数是可被引用的数据对象,例如,变量、数组元素、结构成员、引用和解除引用的指针都是左值。非左值包括字面常量(用引号括起的字符串除外,它们由地址表示)和包含多项的表达式。

/* cubes.cpp--regular and reference arguments */
#include 

using namespace std;
double cube(double a);
double refcube(const double &ra);
int main()
{
    double x = 3.0;
    long edge = 5L;
    cout << cube(x);
    cout << " = cube of " << x << endl;

    cout << refcube(x);
    cout << " = cube of " << x << endl;
    cout << refcube(edge);
    cout << " = cube of " << edge << endl;
    cout << refcube(7.0);
    cout << " = cube of " << "7.0" << endl;
    return 0;

}

double cube(double a)
{
    a *= a * a;
    return a;
}

double refcube(const double &ra)
{
    return ra * ra * ra;
}
27 = cube of 3
27 = cube of 3
125 = cube of 5
343 = cube of 7.0

上述示例中,edge虽然是变量,类型却不正确,double引用不能指向long。另一方面,参数7.0的类型正确,但没有名称,在这些情况下,编译器将生成一个临时匿名变量,并让ra指向它。这些临时变量只在函数调用期间存在,此后编译器便可以随意将其删除

上述代码中如果去掉const会怎么样?

/* cubes.cpp--regular and reference arguments */
#include 

using namespace std;
double cube(double a);
double refcube(double &ra);
int main()
{
    double x = 3.0;
    long edge = 5L;
    cout << cube(x);
    cout << " = cube of " << x << endl;

    cout << refcube(x);
    cout << " = cube of " << x << endl;
    cout << refcube(edge);
    cout << " = cube of " << edge << endl;
    cout << refcube(7.0);
    cout << " = cube of " << "7.0" << endl;
    return 0;

}

double cube(double a)
{
    a *= a * a;
    return a;
}

double refcube(double &ra)
{
    return ra * ra * ra;
}
||=== Build: Debug in firstref (compiler: GNU GCC Compiler) ===|
E:\codeblock_code\c++\reference\firstref\main.cpp||In function 'int main()':|
E:\codeblock_code\c++\reference\firstref\main.cpp|16|error: invalid initialization of non-const reference of type 'double&' from an rvalue of type 'double'|
E:\codeblock_code\c++\reference\firstref\main.cpp|6|note:   initializing argument 1 of 'double refcube(double&)'|
E:\codeblock_code\c++\reference\firstref\main.cpp|18|error: invalid initialization of non-const reference of type 'double&' from an rvalue of type 'double'|
E:\codeblock_code\c++\reference\firstref\main.cpp|6|note:   initializing argument 1 of 'double refcube(double&)'|
||=== Build failed: 2 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|

应尽可能使用const

将引用参数声明为常量数据的引用的理由有三个:

  • 使用const可以避免无意中修改数据的编程错误;
  • 使用const使函数能够处理const和非const实参,否则将只能接受非const数据;
  • 使用const引用使函数能够正确生成并使用临时变量。

因此,应尽可能将引用形参声明为const。

将引用用于结构

/* strc_ref.cpp--using structure references */
#include 
#include 
using namespace std;

struct free_throws
{
    string name;
    int made;
    int attempts;
    float percent;
};

void display(const free_throws &ft);
void set_pc(free_throws &ft);
free_throws & accumulate(free_throws &target, const free_throws &source);

int main()
{
    //partial initializations-remaining members set to 0
    free_throws one = {"Ifelsa Branch", 13, 14};
    free_throws two = {"Andor Knott", 10, 16};
    free_throws three = {"Minnie Max", 7, 9};
    free_throws four = {"Whily Looper", 5, 9};
    free_throws five = {"Long Long", 6, 14};
    free_throws team = {"Throwgoods", 0, 0};
    //no initialization
    free_throws dup;

    set_pc(one);
    display(one);
    accumulate(team, one);
    display(team);
    //use return value as argument
    display(accumulate(team, two));
    accumulate(accumulate(team,three), four);
    display(team);
    //use return value in assignment
    dup = accumulate(team, five);

    cout << "Displaying team:\n";
    display(team);
    cout << "Displaying dup after assignment:\n";
    display(dup);
    set_pc(four);
    //ill-advised assignment
    accumulate(dup, five) = four;
    cout << "Displaying dup after ill-advised assignment:\n";
    display(dup);
    return 0;
}

void display(const free_throws &ft)
{
    cout << "Name: " << ft.name << '\n';
    cout << "  Made: " << ft.made << '\t';
    cout << "Attempts: " << ft.attempts << '\t';
    cout << "Percent: " << ft.percent << '\n';
}

void set_pc(free_throws &ft)
{
    if (ft.attempts != 0)
        ft.percent = 100.0f * float(ft.made) / float(ft.attempts);
    else
        ft.percent = 0;
}

free_throws & accumulate(free_throws &target, const free_throws &source)
{
    target.attempts += source.attempts;
    target.made += source.made;
    set_pc(target);
    return target;
}
Name: Ifelsa Branch
  Made: 13      Attempts: 14    Percent: 92.8571
Name: Throwgoods
  Made: 13      Attempts: 14    Percent: 92.8571
Name: Throwgoods
  Made: 23      Attempts: 30    Percent: 76.6667
Name: Throwgoods
  Made: 35      Attempts: 48    Percent: 72.9167
Displaying team:
Name: Throwgoods
  Made: 41      Attempts: 62    Percent: 66.129
Displaying dup after assignment:
Name: Throwgoods
  Made: 41      Attempts: 62    Percent: 66.129
Displaying dup after ill-advised assignment:
Name: Whily Looper

 

你可能感兴趣的:(c++)