引用和内联函数
一 内联函数
内联函数是C++为提高程序运行速度所做的一项改进。
编译过程的最终产品是可执行程序(由一组机器语言指令组成)。程序运行时,操作系统将这些指令载入到计算机内存中,因此每条指令都有特定的内存地址。
常规函数调用过程---程序跳到函数的地址,并在函数结束时返回。即,程序执行到函数调用指令时,将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈,跳到标记函数起点的内存单元,执行函数代码,返回值放入寄存器中,然后跳回到地址被保存的指令处(这与阅读文章时停下来看标注,并在阅读完标注后返回到之前阅读的地方类似)
内联函数是将相应的代码替换函数调用。优点是内联函数运行速度比常规函数稍快,缺点是占用更多内存。
要使用内联函数,需要:
(1)在函数声明前加上关键字inline
(2)在函数定义前加上关键字inline
注:内联函数不能递归
另:内联函数与宏的区别
内联函数参数传递方式与常规函数一样,宏是简单的文本替换来完成
例如:
inline double square(double x) {return x*x; }
#define SQUARE(X) X*X
SQUARE(0.5) //->0.5*0.5
SQUARE(4.5+7.5) //4.5+7.5*4.5+7.5
SQUARE(c++) //c++*c++
二 引用变量
引用时已定义的变量的别名(如土豆,又叫马铃薯)。引用的主要用途是用作函数的形参。通过引用传递参数,函数会访问使用原始数据,而不是像按值传递一样使用副本。
(1)创建引用变量
int rats; int & rodents = rats;//rodents为rats的引用,即别名 这两个值表明的是同一个事物,同一块内存同一个地址同一个值
特点:与const指针比较接近,必须在声明引用变量时进行初始化。一旦与某个变量关联,就将一直效忠与它。
引用与变量一体,共用同一块内存,看好引用的内存和值即可。数组不能声明引用
(2)将引用作为函数参数
引用作为函数参数,原理是将函数中变量名成为调用程序中的变量的别名。
按值传递 void sneezy(int x); int main() { int times = 20;//创建times变量,并赋值20
sneezy(times); }
void sneezy(int x)//创建x变量,将传递来的值赋值给x
{
...
}
2个变量,两个名字,x和times除了值相同,完全两个变量,没有任何联系
按引用传递 void grumpy(int &x); int main() { int times = 20;//创建times变量,赋值20
grumpy(times); }
void grumpy(int &x)//x为times的别名
{...}
times和x是同一个变量,只不过有两个名称
函数调用使用实参初始化形参,因此函数的引用参数被初始化为函数调用传递的实参。
(3)关于引用的临时变量和const引用
如果实参和引用参数类型不匹配,C++将生成临时变量。仅当参数为const引用时,C++才允许这样使用不会报错。
关于临时变量,如果引用参数时const,则下面两种情况下生成临时变量(匿名变量)
实参的类型正确,但不是左值。
实参的类型不正确,但可以转换为正确的类型
double refcube(const double & ra)//仅当为常量引用,可以允许下面的情况,因为不修改ra的值,所以是不是临时变量无所谓 { return ra * ra * ra; } int main() { double side = 3.0; double * pd = &side; double & rd = side; long edge = 5L; double lens[4] = {2.0, 5.0, 10.0, 12.0}; refcube(side); refcube(lens[2]); refcube(*pd); refcube(edge);//生产临时变量 refcube(7.0);//生产临时变量 refcube(side+7.0);//生产临时变量 const double & p12 = 0.1+0.2; system("pause"); return 0; }
为什么非const引用不可以这样操作?
理解如下:
void swapr(int &a, int &b) { int temp; temp = a;
a = b;
b = temp; }
long a = 3, b = 5;
swapr(a,b);
上述实参类型和形参的引用类型不匹配,编译器会创建两个临时int变量,将其初始化为3和5,然后交换临时变量的值,实参的值不变
一个接受引用参数的函数的目的就是修改实参的值,如果创建临时变量,就阻止了函数的这一作用。
实际上,对于形参为const引用的C++函数,如果实参不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将使用临时变量来存储值。
将引用参数声明为常量数据的引用的理由:
1)使用const可以避免无意中修改数据的编程错误
2)使用const使函数能够处理const和非const实参,否则将只能接受非const数据
3)使用const引用能使函数能够正确生成并使用临时变量
(4)将引用用于结构
引用非常适合用于结构和类,引用主要是为了用于这些类型,而不是基本的内置类型
struct free_throws
{
std::string name;
int made;
int attempts;
float percent;
}
free_throws dup;
free_throws four = {"Whily Looper", 5, 9};
free_throws five = {"Long Long", 5, 9};
free_throws & accumulate(free_throws & target, const free_throws & source);
accumulate(dup, five) = four;//不常用,作为一个例子解释下面问题
1.函数形参为引用:与按值传递相比,引用传递少了复制拷贝原始结构这一步,可以节省时间和内存。
2.函数返回值为引用:传统返回机制会计算return后变量(这个值被复制到一个临时位置),并将结果返回给调用函数,
例:dup = accumulate(four, five);
如果accumulate返回一个结构,不是引用,会将整个结构复制到一个临时位置,再将这个拷贝复制给dup
现在是引用,会直接把函数中return的值复制给dup,效率更高
4.返回引用时应注意:避免返回函数终止时不再存在的内存单元引用。1)返回一个作为参数传递给函数的引用 2)用new来分配新的存储空间,返回指向该空间的指针
5.可以将const用于引用返回类型,避免返回值被改变
accumulate(dup, five) = four;可以通过编译,但是若此函数返回值为结构体,则不能通过编译
原因:
在赋值语句中,左边必须是可以修改的左值,即左边的子表达式必须标识一个可修改的内存块,此函数返回值为结构体引用时,函数返回dup的引用,它确实标识的时一个这样的内存块,所以
可以编译通过
常规(非引用)返回类型是右值---不能通过地址访问的值,这种表达式可以出现在赋值语句的右边,但是不能出现在左边。其他右值包括字面值如10.0和表达式如x+y。获取字面值的地址
没有意义,但是为何常规函数返回值是右值呢,因为这种返回值位于临时内存单元中,运行到下一条语句时,它们可能就不在存在了。
(5)将引用用于类对象
类中有一个特征,基类引用可以指向派生类对象,无需进行强制类型转换。
可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作用参数,也可以将派生类对象作为参数。