引用与结构
引用非常适合用于结构和类, 使用结构引用参数的方式与使用基本变量相同, 只需要声明结构参数时使用引用运算符&即可, 如果不希望函数修改传入的结构, 可以使用const
来看一个简单的demo
#include <iostream>
#include <string>
using namespace std;
// 声明一个结构体
struct st1
{
string name;
int made;
int attempts;
float percent;
};
// 只是用来显示, 所以使用const引用
// 使用引用的原因是为了节省内存
void display(const st1 & ft);
// 没有使用const是为了在函数内部修改结构体的内容
void set_pc(st1 & ft);
// 返回一个结构体的引用
// 由于只需要修改第一个结构体的内容, 所以第二个结构体引用为const
st1 & accumulate(st1 & target, const st1 & source);
int main()
{
// 创建几个结构体变量, 并初始化
// 如果初始值比成员值少, 剩余的成员将被设置成0
st1 one = {"one", 1, 11};
st1 two = {"two", 2, 22};
st1 three = {"three", 3, 33};
st1 four = {"four", 4, 44};
st1 five = {"five", 5, 55};
st1 team = {"team", 0, 0};
// 不进行初始化
st1 dup;
// 默认由于没有对percent进行初始化, 所以percent是0
display(one);
// 设置percent
set_pc(one);
display(one);
// 将one里的made和attempts加到team中
accumulate(team, one);
display(team);
display(accumulate(team, two));
accumulate(accumulate(team, three), four);
display(team);
dup = accumulate(team, five);
cout << "Displaying team : " << endl;
display(team);
cout << "Displaying dup after assignment : " << endl;
display(dup);
set_pc(four);
// 注意这个写法, 相当于:
// accumulate(dup, five); dup = four; 这两句的合并
accumulate(dup, five) = four;
cout << "Displaying dup after ill-advised assigment :" << endl;
display(dup);
return 0;
}
void display(const st1 & ft)
{
cout << "Name = " << ft.name;
cout << ", made = " << ft.made;
cout << ", attempts = " << ft.attempts;
cout << ", percent = " << ft.percent << endl;
}
void set_pc(st1 & ft)
{
if (ft.attempts != 0)
ft.percent = 100.0f * float(ft.made) / float(ft.attempts);
else
ft.percent = 0;
}
st1 & accumulate(st1 & target, const st1 & source)
{
target.attempts += source.attempts;
target.made += source.made;
set_pc(target);
return target;
}
程序运行结果为:
需要注意的地方都用注释写明了, 着重注意一点:
accumulate(dup, five) = four;
相当于这两句的合并:
accumulate(dup, five);
dup = four;
返回引用与返回值的区别
st1 & accumulate(st1 & target, const st1 & source);
dup = accumulate(team, five);
返回值的话, 相当于将返回结果复制到一个临时变量中, 然后将临时变量copy复制给接收处, 而引用实际是直接把team复制到dup, 其效率更高.
注意: 返回引用的函数实际是被引用的变量的别名.
返回引用时应注意的问题
应避免返回 返回函数执行完毕时不存在的内存单元, 例如:
const st1 & clone(st1 & ft)
{
st1 newguy;
// 复制
newguy = ft;
// 返回引用
return newguy;
}
该函数返回的是一个临时变量(newguy)的引用, 当函数执行完毕的时候newguy将不在存在, 所以返回这种引用是不行的.
为避免这种问题, 最简单的方法是返回一个作为参数传递给函数的引用.
另一种方法是用new来分配新的存储控件.
const st1 & clone2(st1 & ft)
{
st1 * pt;
// 复制
*pt = ft;
// 返回引用
return *pt;
}
st1 * pt; 创建了一个无名的st1结构, 并让指针pt指向该结构体, 因此*pt就是该结构体. 可以这么使用该函数:
st1 & jolly = clone2(three);
这使得jolly成为新的结构体的引用, 但是有个问题, 就是在不再需要new分配的内存时, 我们应该使用delete来释放他们, 调用clone隐藏了对new的调用, 因此不要忘记使用完毕后dellete
常规返回类型是右值--不能通过地址访问的值, 这种表达式可出现在赋值语句的右边, 但是不能出现在左边, 其他的右值包括字面值(如10.0, 和表达式x + y), 显然, 获取字面值的地址没有意义. 但为何常规和桉树返回值是右值呢? 是因为这种返回值位于临时内存单元中, 运行到下一条语句时, 他们可能不再存在.