引用,是某个已存在变量的另外一个名字。ios
一旦把引用初始化为某个变量,就可使用该引用名称或变量名称来指向变量。编程
注意:函数
引用没有定义,是一种关系型声明。声明它和原有某一变量(实体)的关
系。所以引用类型必须与原类型保持一致,且不分配内存。与被引用的变量有相同的地
址。this
& 符号前有数据类型时,是引用。其它皆为取地址。spa
可对引用再次引用。屡次引用的结果,是某一变量具备多个别名。指针
建立引用例子以下:code
// reference.cpp #include <iostream> using namespace std; int main () { // 声明简单的变量 int i; double d; // 声明引用变量 int& r = i; double& s = d; i = 1; cout << "Value of i : " << i << endl; cout << "Value of i reference : " << r << endl; d = 6.1; cout << "Value of d : " << d << endl; cout << "Value of d reference : " << s << endl; getchar(); return 0; }
运行结果:对象
引用编程实践以下:blog
// reference2.cpp #include <iostream> using namespace std; int main() { int a = 1; int& b = a; // b = a = 1 a = 2; int *p = &a; *p = 3; // a = 3 cout << "a = " << a << endl; b = 4; // b = a -> a = 4 cout << "a = " << a << ", b = " << b << endl; getchar(); return 0; }
运行结果:内存
引用很容易和指针混淆,它们之间有如下不一样:
不存在空引用。引用必须链接到一块合法的内存。指针能够为空指针。
引用必须在建立时被初始化(引用做为函数参数的时候不须要初始化,由于形参必定会被赋值的)。指针能够在任什么时候间被初始化。
一旦引用被初始化为一个对象,就不能被指向到另外一个对象。指针能够在任什么时候候指向到另外一个对象。
引用做为其余变量的别名而存在,所以在一些场合能够代替指针
引用相对于指针来讲具备更好的可读性和实用性
// 没法实现两数据的交换 void swap(int a,int b); //开辟了两个指针空间用于交换 void swap(int *a,int *b);
// referenceSwap.cpp,不开辟空间使用引用进行数值交换 #include <iostream> using namespace std; void swap(int& a, int& b) { int temp; temp = a; a = b; b = temp; } int main() { int a = 1,b = 2; cout << "a = " << a << " , b = " << b << endl; swap(a,b); cout << "a = " << a << " , b = " << b << endl; getchar(); return 0; }
运行结果:
全局变量、静态局部变量、静态全局变量,new 产生的变量都在堆中,动态分配的变量在堆中分配。
局部变量在栈里面分配。
程序为栈变量分配动态内存,在程序结束为栈变量清除内存,可是堆变量不会被清除。
当函数返回值为引用时:
以下代码所示:
// funcReturnRef.cpp,引用做为函数的返回值,何时能够为其余引用初始化的值 #include <iostream> using namespace std; // 返回栈变量 int getA1() { int a; a = 1; return a; } // 返回栈变量引用 int& getA2() { int a; a = 1; return a; } int main() { int a1 = 0; int a2 = 0; // 值拷贝 a1 = getA1(); // 将 栈变量引用 赋值给 变量,编译器相似作了以下隐藏操做:a2 = *(getA2()) a2 = getA2(); // 将 栈变量引用 赋值给 另外一个引用做为初始值。此时将会有警告:返回局部变量或临时变量的地址 int& a3 = getA2(); cout << "a1 = " << a1<< endl; cout << "a2 = " << a2<< endl; cout << "a3 = " << a3<< endl; getchar(); return 0; }
警告信息:
第一次运行结果:
第二次运行结果:
以下代码所示:
// funcReturnRef2.cpp,引用做为函数的返回值,何时能够为其余引用初始化的值 #include <iostream> using namespace std; // 返回堆变量 int getA1() { static int a; a = 1; return a; } // 返回栈变量引用 int& getA2() { static int a; a = 1; return a; } int main() { int a1 = 0; int a2 = 0; // 值拷贝 a1 = getA1(); // 将 栈变量引用 赋值给 变量,编译器相似作了以下隐藏操做:a2 = *(getA2()) a2 = getA2(); // 将 堆变量引用 赋值给 另外一个引用做为初始值。因为是静态区域,地址不变,内存合法。 int& a3 = getA2(); cout << "a1 = " << a1<< endl; cout << "a2 = " << a2<< endl; cout << "a3 = " << a3<< endl; getchar(); return 0; }
运行结果:
C++ 中指针引用做函数参数,与 C 语言中二级指针做函数参数的区别。
以下代码所示:
// pointReference.cpp // C++ 中指针引用做函数参数,与 C 语言中二级指针做函数参数的区别 #include <iostream> using namespace std; #define AGE 18 // C 语言中的二级指针 int getAge1(int **p) { int age = AGE; int *ptemp = &age; // p 是实参的地址, *实参的地址,去间接的修改实参 *p = ptemp; return 0; } // C++ 中指针引用 int getAge2(int* &p) { int age = AGE; if(p == NULL) { p = (int *)malloc(sizeof(int)); if(p == NULL) return -1; } // 给 p 赋值,至关于给 main 函数中的 pAge 赋值 *p = age; return 0; } int main(void) { int *pAge = NULL; // 1 C 语言中二级指针 getAge1(&pAge); cout << "age: " << *pAge << endl; pAge = NULL; // C++ 中指针引用 getAge2(pAge); cout << "age: " << *pAge << endl; pAge = NULL; getchar(); return 0; }
运行结果:
const 对象的引用必须是 const 的。
const 引用可使用相关类型的对象(常量,非同类型的变量或表达式)初始化。这个是 const 引用与普通引用最大的区别。
例:
const int &a = 1; double x = 1.1; const int &b = x;
C 语言中有宏函数的概念。宏函数的特色是内嵌到调用代码中去,避免了函数调用的开销。可是因为宏函数的处理发生在预处理阶段,确实了语法检测和有可能带来的语义差错,所以 C++ 引入了 inline 内联函数。
内联函数声明时 inline 关键词必须和函数定义结合在一块儿,不然编译器会忽略内联请求。
C++ 编译器直接将函数体插入在函数调用的地方。
内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)
内联函数是一种特殊的函数,具备普通函数的特征(参数检查,返回类型等)
内联函数由编译器处理,直接将编译后的函数体插入在调用的地方,宏函数由预处理器处理,进行简单的文本替换,没有任何的编译过程。
C++ 对内联函数的限制:
编译器对于内联函数的限制不是绝对的,内联函数相对于普通函数的优点知识节省了函数调用时压栈,跳转,和返回的开销。所以,当函数体的执行开销远大于压栈,跳转,和返回的开销时,那么内联函数将没有意义。
实例代码以下所示:
// inlineFunction.cpp // 内联函数示例 #include <iostream> using namespace std; inline void func() { cout << "this is inlineFunction example!" << endl; } int main() { func(); getchar(); return 0; }
运行结果:
函数重载:用同一个函数名定义不一样的函数,当函数名和不一样的参数搭配时函数的含义不一样。
函数名相同
参数个数不一样,参数的类型不一样,参数顺序不一样,都可构成重载。
返回值类型不一样则不能够构成重载。
以下所示:
void func(int a); // ok void func(char a); // ok void func(char a,int b); // ok void func(int a,char b); // ok char func(int a); // 与第一个函数冲突,报错
严格匹配,找到即调用。
经过隐式转换寻求一个匹配,找到即调用。
C++ 利用 name mangling(倾轧)技术,来改变函数名,以区分参数不一样的同名函数。
实现原理:用 v c i f l d 表示 void char int float long double 及其引用。
以下所示:
void func(char a); // func_c(char a); void func(char a,int b,double c); // func_cid(char a,int b,double c);
// 方法一: // 声明一个函数类型 typedef void (myfunctype)(int a,int b); // 定义一个函数指针 myfunctype* fp1= NULL; // 方法二: // 声明一个函数指针类型 typedef void (*myfunctype_pointer)(int a,int b) // 定义一个函数指针 myfunctype_pointer fp2 = NULL; // 方法三: // 直接定义一个函数指针 void (*fp3 )(int a,int b);
当使用重载函数名对函数指针进行赋值时,根据重载规则挑选与函数指针参数列表一致的候选者,严格匹配候选者的函数类型与函数指针的函数类型。
示例代码以下所示:
#include <iostream> using namespace std; void func(int a, int b) { cout << a << b << endl; } void func(int a, int b, int c) { cout << a << b << c << endl; } void func(int a, int b, int c, int d) { cout << a << b << c << d << endl; } // 1 定义一个函数类型 typedef void(myfunctype)(int, int); //定义了一个函数类型, 返回值void 参数列表是 int,int ,, void()(int,int) // 2 定义一个函数指针类型 typedef void(*myfunctype_pointer)(int, int); //定义了一个函数指针类型, 返回值void 参数列表是 int,int ,, void(*)(int,int) int main(void) { //1 定义一个函数指针 myfunctype * fp1 = NULL; fp1 = func; fp1(10, 20); // 2 定义一个函数指针 myfunctype_pointer fp2 = NULL; fp2 = func; fp2(10, 20); // 3 直接定义一个函数指针 void(*fp3)(int, int) = NULL; fp3 = func; fp3(10, 20); cout << " -----------------" << endl; // 此时的fp3 是 void(*)(int,int) // fp3(10, 30, 30); // fp3 恒定指向一个 函数入口,void func(int, int) 的函数入口 // fp3(10, 30, 40, 50); // 想要经过函数指针,发生函数重载 是不可能。 fp3(10, 20); void(*fp4)(int, int, int) = func; // 在堆函数指针赋值的时候,函数指针会根据本身的类型 找到一个重载函数 fp4(10, 10, 10); // fp4(10, 10, 10, 10); // 函数指针,调用的时候是不可以发生函数重载的。 void(*fp5)(int, int, int, int) = func; // void func(int ,int ,int ,int ) fp5(10, 10, 10, 10); return 0; }
运行结果:
重载函数在本质上是相互独立的不一样函数。
函数重载是由函数名和参数列表决定的。