目录
指针
引用
将引用用作函数参数
引用的属性和特别之处
临时变量 引用参数和const
应尽可能使用const
将引用用于结构
指针持有一个对象的地址,称为指针"指向"这个对象。通过指针可以间接操纵它指向的对象,指针的典型用法包括:
每个指针都有相关的类型,需要在定义指针时指出。指针的类型指出了如何解释该内存地址保存的内容,以及该内存区域应该有多大。
引用又称为别名,它可以作为对象的另一个名字。通过引用可以间接地操作的对象。在程序中,引用主要用作函数的参数。
引用由类型标识符合一个取地址符(&)来定义,引用必须被初始化,初始值是一个有内存地址的对象,如变量。
#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)) ===|
如果实参与引用参数不匹配,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。
/* 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