1.内联函数
内联函数是C++为提高程序运行速度所做的一项改进。常规函数和内敛函数之间的主要却别在于编写方式,而在于编译器如何将他们组合到程序中。要了解内联函数与常规函数之间的区别,必须深入到程序的内部。
执行到函数调用指令时,程序将在函数调用后立即存储指令的内存地址,不能够将函数参数复制到堆栈(为此保留的对战块),跳到标记函数起点的内存单元,执行函数代码(也许还需要将返回值放入到),然后跳回到地址被保存的指令处
内联函数的编译代码与其他程序代码“内联”起来了。也就是说,编译器将使用相应的函数代码替换函数调用。对于内联代码,函数无需跳到另一个位置处执行代码,然后再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多的内存。
- //Demo1 内联函数
- #include
- inline double square(double x)
- {
- return x*x;
- }
- int main()
- {
- double a,b;
- double c = 13.0;
- a = square(5.0);
- b = square(4.5+7.5);
- return 0;
- }
2.引用操作符(&)、地址操作符(&)和指针操作符(*)的区别
int rats = 101;
int &rodents = rats;//引用变量
int *prats = &rats;//指针变量
rodents和*prats都可以同rats互换,&rodents和prats都可以同&rats互换。
引用必须在声明时将其初始化
引用更接近const指针,必须在创建时进行初始化。
比如:int &rodents = rats;实际上是int * const r = &rats;的伪装表示,rodents扮演的角色与表达式*pr相同。
- //Demo2 初始化引用变量
- #include
- int main()
- {
- using namespace std;
- int rats = 101;
- int &rodents = rats;
- cout<<"rats = "<<rats;
- cout<<",rodents = "<<rodents<<endl;
- cout<<"rats address = "<<&rats;
- cout<<",rodents address = "<<&rodents<<endl;
- int bunnies = 50;
- rodents = bunnies;//可以通过初始化来设置引用,但不能够通过赋值来改变引用,这里虽然rats,rodents的值改变了,但rodents的引用没改变。
- cout<<"bunnies = "<<bunnies;
- cout<<",rats = "<<rats;
- cout<<",rodents = "<<rodents<<endl;
- cout<<"bunnies address = "<<&bunnies;
- cout<<",rodents address = "<<&rodents<<endl;
- return 0;
- }
3.在函数调用时,如果实参与引用参数不匹配,C++将生成临时变量,仅当参数为const引用时,C++才允许这样做。
4.引用非常适合用于结构和类,引入引用主要是为了用于这些类型的,而不是基本的内置类型。
- //Demo4 结构引用
- #include
- using namespace std;
- typedef struct sysop
- {
- char name[26];
- char quote[64];
- int used;
- };
- const sysop & use(sysop & sysopref);
- int main()
- {
- sysop looper =
- {
- "Rick /"Fortran/" Lopper",
- "I'm a goto kind of guy.",
- 0
- };
- use(looper);
- cout<<"Lopper:"<<looper.used<<" use(s)/n";
- sysop copycat;
- copycat = use(looper);
- cout<<"Lopper:"<<looper.used<<" use(s)/n";
- cout<<"Copycat:"<<copycat.used<<" use(s)/n";
- cout<<"use(lopper):"<<use(looper).used<<" use(s)/n";
- return 0;
- }
- const sysop & use(sysop & sysopref)
- {
- cout<<sysopref.name<<" says:/n";
- cout<<sysopref.quote<<endl;
- sysopref.used ++;
- return sysopref;
- }
5.应当避免函数使用返回引用临时变量的引用,以及返回指向临时变量的指针。否则会返回一个空引用或空指针。
6.何时使用引用参数能够修改调用函数中的数据对象
- 能够改掉函数中的数据对象
- 通过传递引用而不是整个数据对象,可以提高程序的运行速度
7.函数参数什么时候使用引用?什么时候使用指针?什么时候用值传递?
- 如果数据对象很小,如内置数据类型或小型结构,则按值传递。
- 如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针。
- 如果数据对象是较大的结构,则使用const指针或const引用,以提高程序的效率。这样可以节省赋值结构所需的时间和空间。
- 如果数据对象是类对象,则使用const引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因。因此,传递类对象参数的标准方式是按引用传递。
- 如果数据对象是内置数据类型,则使用指针。如果看到诸如fixit(&x)这样的代码(其中x是int型),则很明显,函数将修改x。
- 如果数据对象是数组,则只能使用指针。
- 如果数据对象是结构,则使用引用或指针。
- 如果数据对象是类对象,则使用引用。
- //Demo3 引用传递、指针专递和值传递
- #include
- void swapr(int &a,int &b);
- void swapp(int *p,int *q);
- void swapv(int a,int b);
- int main()
- {
- using namespace std;
- 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:/n";
- swapp(&wallet1,&wallet2);
- cout<<"wallet1 = $"<<wallet1;
- cout<<",wallet2 = $"<<wallet2<<endl;
- return 0;
- }
- void swapr(int &a,int &b)
- {
- 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;
- }
8.函数的默认参数
char* left(const char * str,int n = 1);
使用默认参数可以减少定义的析构函数、方法以及方法重载的数量。可以定义多个函数默认参数,上面的函数声明中,在调用函数时,如果不传入参数left()调用,则在函数内部会采用n=1来实现。
9.函数的模板
函数模板时通用的函数描述,可以使用通用类型来定义函数,通用类型又可以用具体类型来替换。通过将类型作为参数传递给模板,可使编译器生成该类的函数。Java的泛型很像C++的模板函数,不过Java的泛型只支持对象类型,不支持基本数据类型的泛型。函数的模板可以自动帮我们完成重载的过程。
- 函数模板原型声明:
template
void swap(T &a,T &b);
关键字template和class是必须的,可以用typename代替class,模板函数也支持重载
- Code Snippet
- //Demo5 C++函数模板
- #include
- template<class T>
- void Swap(T &a,T &b);
- int main()
- {
- using namespace std;
- int i = 10;
- int j = 20;
- Swap(i,j);
- cout<<"i,j = "<<i<<","<<j<<"./n";
- return 0;
- }
- template<class T>
- void Swap(T &a,T &b)
- {
- T temp;
- temp = a;
- a = b;
- b = temp;
- }
- 显示具体化(explicit specialization),当编译器找到与函数条用匹配的具体化定义时,将使用该定义,而不再寻找模板。
具体化标准
1.对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及它们的重载
2.显示具体化的原型和定义应以template<>开头,并通过名称来指出类型
3.具体化将覆盖常规模板,而非模板函数将覆盖具体化和常规模板。 - Code Snippet
- //Demo6 函数模板之显式具体化
- #include
- template <class T>
- void Swap(T &a,T &b);
- struct job
- {
- char name[40];
- double salary;
- int floor;
- };
- template <> void Swap<job>(job &j1,job &j2);//这里也可以写成template<>void Swap(job &j1,job &j2);
- void show(job &j);
- int main()
- {
- using namespace std;
- cout.precision(2);
- cout.setf(ios::fixed,ios::floatfield);
- int i = 10;
- int j = 20;
- cout<<"i,j = "<<i<<","<<j<<endl;
- cout<<"Using compiler-generated in swapper:/n";
- Swap(i,j);
- cout<<"Now i,j = "<<i<<","<<j<<endl;
- job sue ={"Susan Yaffee",7300.60,7};
- job sidney = {"Sidney Taffee",78060.72,9};
- cout<<"Before job swapping:/n";
- show(sue);
- show(sidney);
- //声明了两个模板函数,但是编译器会按照非模板函数->显式具体化模板函数->非显式具体化模板函数的顺序去查找函数定义
- Swap(sue,sidney);
- cout<<"After job swapping:/n";
- show(sue);
- show(sidney);
- return 0;
- }
- //普通函数模板
- template <class T>
- void Swap(T &a,T &b)
- {
- T temp;
- temp = a;
- a = b;
- b = temp;
- }
- //函数模板显式具体化
- template <> void Swap<job>(job &j1,job &j2)
- {
- double t1;
- int t2;
- t1 = j1.salary;
- j1.salary = j2.salary;
- j2.salary = t1;
- t2 = j1.floor;
- j1.floor = j2.floor;
- j2.floor = t2;
- }
- void show(job &j)
- {
- using namespace std;
- cout<<j.name<<":$"<<j.salary
- <<" on floor "<<j.floor<<endl;
- }
- 隐式实例化和显示实例化
在调用普通模板Swap(5,6)时,编译器会生成一个Swap()的实例,该实例为int型,同样Swap(2.56,3.87)会生成一个double型的Swap()实例,这种实例化的方式是编译器去做的,也叫隐式实例化。C++现在允许显式实例化,显式实例化的函数声明和显式具体化的声明很像,不过有一点点区别,显式实例化比显式具体化在关键字template后少了一个<>。
template void Swap (int,int);//显式实例化
template<>void Swap (int,int);//显式具体化