内联函数的编译代码与其他程序代码 “内联” 起来了,编译器将使用相应的函数代码替换函数调用;
对于内联函数,程序无需跳到另一个位置处执行代码,再跳回来:
内联函数的运行速度比常规函数稍快;
但是需要占用更多的内存
使用内联函数:
1. 在函数声明前加上关键字 inline;
2. 在函数定义前加上关键字 inline;
3. 通常做法是省略原型,将整个定义(函数头和所有函数代码)放在本应提供原型的地方
内联函数与常规函数一样,也是按值传递参数
C++新增了一种复合类型——引用变量,引用是已定义的变量的别名;
引用变量的主要用途是用作函数的形参,通过引用变量用作参数,函数使用原始数据,而不是拷贝数据;
int rats;
int & rodents = rats; // 此处引用声明允许将 rats 和 radents 互换——他们指向相同的值和内存单元
int & 表示的是指向 int 的引用
记住:必须在声明引用时将其初始化(所以引用更接近与 const 指针)
按引用传递允许被调用函数能够访问调用函数中的变量(C语言只能按值传递(还可以指针)——拷贝);
参数传递对比:
调用:
swapr ( wallet1, wallet2); // 引用传递调用
swapp ( &wallet1, &wallet2 ); // 指针传递调用
awapv (wallet1, wallet2); // 按值传递调用
原型:
void ( int & a, int & b); // 引用调用原型,变量a,b是 wallet1 和 wallet2 的别名
void ( int * a, int * b); // 指针传递原型,需要在函数使用 p 和 q 的整个过程使用解除引用操作符 *
void ( int a, int b); // 按值传递原型,变量 a,b 是参数 wallet1 和 wallet2 的拷贝,互补影响
被调用函数中引用变量修改,会改变调用函数中的值,如不想修改则应使用常量引用:
double refcube ( const double &ra ); // 这样做,当编译器发现代码修改了 ra 的值时,将发生错误消息
如果实参与形参不匹配,C++将生成临时变量(仅当参数为 const 引用时):
1. 实参的类型正确,但不是左值;
2. 实参的类型不正确,但可以转换成正确的类型
1. 使用 const 可以避免无意中修改数据的编程错误;
2. 使用 const 使函数能够处理 const 和非 const 实参,否则将只能接受非 const 数据;
3. 使用 const 引用使函数能够正确生成并使用临时变量
使用引用参数的原因:
1. 程序员能够修改调用函数中的数据对象;
2. 通过传递引用而不是整个数据对象,可以提高程序运行速度(数据对象较大时更重要)
对于使用传递的值而不作修改的函数:
1. 如果数据对象很小,如内置数据类型或小型结构,按值传递;
2. 如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向 const 的指针;
3. 如果数据对象是较大的结构,则使用 const 指针或 const 引用,可以节省复制结构所需的时间和空间;
4. 若果数据对象是类对象,则使用 const 引用。传递类对象参数的标准方式是按引用传递
对于修改调用函数中数据的函数:
1. 如果数据对象是内置数据类型,则使用指针;
2. 如果数据对象是数组,则只能使用指针;
3. 如果数据对象是结构,则使用引用或指针;
4. 如果数据对象是类对象,则使用引用
引用非常适用于结构和类(C++的用户自定义类型),引入引用主要是为了用于这些类型,而不是基本的内置类型;
例程:
1 #include<iostream> 2 using namespace std; 3 struct sysop { 4 char name[26]; 5 char quote[64]; 6 int used; 7 }; 8 const sysop & use(sysop & sysopref); 9 10 int main() 11 { 12 sysop looper = 13 { 14 "Rick \"Fortan\" Looper", 15 "I`m a goto kind of guy.", 16 0 17 }; 18 19 use(looper); 20 cout << "Looper: " << looper.used << " use(s)\n"; 21 22 sysop copycat; 23 copycat = use(looper); 24 cout << "Looper: " << looper.used << " use(s)\n"; 25 cout << "copycat: " << copycat.used << " use(s)\n"; 26 cout << "use(looper): " << use(looper).used << " use(s)\n"; 27 28 return 0; 29 } 30 31 const sysop & use(sysop & sysopref) 32 { 33 cout << sysopref.name << " says:\n"; 34 cout << sysopref.quote << endl; 35 sysopref.used++; 36 return sysopref; 37 }
函数说明:
1. 使用指向结构的引用
use ( looper );
函数调用结构 looper 按引用传递给 use() 函数,使得 sysopref 成为 looper 的别名
2. 将引用作为返回值
通常,返回机制将返回值复制到临时存储区域中,随后调用程序将访问该区域;
返回引用意味着调用程序将直接访问返回值,而不需要拷贝;
通常,引用将指向传递给函数的引用,因此调用函数实际上是直接访问自己的一个变量
记住: 返回引用的函数实际上是被引用变量的别名
3. 使用函数调用来访问结构成员
cout << "use(looper): " << use(looper).used << " use(s)\n;
函数 use() 返回一个指向 looper 的引用,因此上述代码与下面两行代码等效:
use ( looper );
cout << " use(looper): " << looper.used << " use(s) \n ";
避免返回当函数终止时不再存在的内存单元引用(返回引用时最重要的一点)
1. 返回一个作为参数传递给函数的引用,将指向调用函数使用的数据,因此返回的引用也指向这些数据
2. 使用 new 来分配新的存储空间:
sysop * psysop = new sysop;
const sysop & use(sysop & sysopref);
const sysop & 表示不能使用返回的引用来直接修改它指向的结构
省略 const 的情况:
use ( looper ).used = 10;
由于 use() 返回一个指向 looper 的引用,上述代码将与下面的代等效:
use ( looper );
looper.used = 1;
省略 const 后,可以编写更简短但含义更模糊的代码
通常,将返回类型声明为 const 引用,可以减程序的模糊特性
将类对象传递给函数时,C++通常做法是使用引用。例如可以通过使用引用,让函数将类 string、ostream、istream、ofstream和ifstream等类对象作为参数
将引用用于 string 类例程:
1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 string version1(const string & s1, const string & s2); 6 const string & version2(string & s1, const string & s2); 7 //const string & version3(string & s1, const string & s2); 8 9 int main() 10 { 11 string input; 12 string copy; 13 string result; 14 15 cout << "Enter a string: "; 16 getline(cin, input); 17 copy = input; 18 cout << "Your string as entered: " << input << endl; 19 result = version1(input, "***"); 20 cout << "Your string enhanced: " << result << endl; 21 cout << "Your original string: " << input << endl; 22 23 result = version2(input, "***"); 24 cout << "Your string enhanced: " << result << endl; 25 cout << "Your original string: " << input << endl; 26 27 return 0; 28 } 29 30 string version1(const string & s1, const string & s2) 31 { 32 string temp; 33 temp = s2 + s1 + s2; 34 return temp; 35 } 36 37 const string & version2(string & s1, const string &s2) 38 { 39 s1 = s2 + s1 + s2; 40 return s1; 41 }
可以将C-风格字符串用作 string 对象引用参数:
如果形参类型为 const string &,在调用函数时,使用的实参可以是 string 对象或C-风格字符串:
因此代码 result = version1(input, "***"); 将 "***" 赋给string对象是可行的
将特性从一个类传递给另一个类的语言特性被称为继承;
ostream 是基类,ofstream 是派生类,派生类继承了基类方法,意味着 ofstream 可以使用基类的特性,如格式化方法 precision(),setf();
基类引用可以指向派生类对象,而无需进行强制类型转换:
可以定义一个接收基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数也可以将派生类对象作为参数;
参数类型为 ostream & 的函数可以接受 ostream 对象(如 cout)或自己声明的 ofstream 对象作为参数
例程:
1 #include<iostream> 2 #include<fstream> 3 #include<cstdlib> 4 using namespace std; 5 6 void file_it(ostream & os, double fo, const double fe[], int n); 7 const int LIMIT = 5; 8 9 int main(void) 10 { 11 ofstream fout; 12 const char * fn = "ep-data.txt"; 13 fout.open(fn); 14 if (!fout.is_open()) 15 { 16 cout << "Can`t open " << fn << ". Bye.\n"; 17 exit(EXIT_FAILURE); 18 } 19 double objective; 20 cout << "Enter the focal length of your telescope objective in mm: "; 21 cin >> objective; 22 double eps[LIMIT]; 23 cout << "Enter the focal lengths, in mm, of " << LIMIT << " eyepieces:\n"; 24 for (int i = 0; i < LIMIT; i++) 25 { 26 cout << "Eyepiece #" << i + 1 << ": "; 27 cin >> eps[i]; 28 } 29 file_it(fout, objective, eps, LIMIT); 30 file_it(cout, objective, eps, LIMIT); 31 cout << "Done\n"; 32 return 0; 33 } 34 35 void file_it(ostream & os, double fo, const double fe[], int n) 36 { 37 ios_base::fmtflags initial; 38 initial = os.setf(ios_base::fixed); // save initial formatting state 39 os.precision(0); 40 os << "Focal length of objective: " << fo << " mm\n"; 41 os.setf(ios::showpoint); 42 os.precision(1); 43 os.width(12); 44 os << "f eyepiece"; 45 os.width(15); 46 os << "magnification" << endl; 47 for (int i = 0; i < n; i++) 48 { 49 os.width(12); 50 os << fe[i]; 51 os.width(15); 52 os << int(fo / fe[i] + 0.5) << endl; 53 } 54 os.setf(initial); // restore initial formatting state 55 }
file_it (fout, objective, eps, LIMIT); // 将目镜数据写入到文件 ep-data.txt 中
file_it (cout, objective, eps, LIMIT); // 将目镜数据显示到屏幕上
程序演示了如何使用 ostream 类中的格式化方法:
方法 setf() 能够设置各种格式化状态:
setf (ios_base::fixed) 将对象置于使用定点表示法的模式;
setf (ios_base::showpoint) 将对象置于显示小数点的模式
方法 precision() 指定此案时多少位小数(假定对象处于定点模式下);
方法 width() 设置下一次输出操作使用的字符段宽度,只在显示下一个值时有效,然后将恢复默认设置。
每个对象都存储了自己的格式化设置,因此,当程序将 cout 或者 fout 传递给 file_it 时,先修改格式化设置再恢复
默认参数指的是当函数调用中省略了实参时自动使用的一个值:
char * left ( const char * str,int n = 1 ):
默认参数值是初始化值,因此原型将 n 初始化为 1,调用函数 left 如果省略参数 n,则它的值为1;
如果没有省略参数 n 的传递,则传递值将覆盖默认参数值
对于带参数列表的函数,必须从右向左添加默认值;
实参按从左到右的顺序依次被赋给相应的形参;
通过使用默认参数可以减少要定义的析构函数、方法以及方法重载的数量
函数多态(函数重载),让我们可以使用多个同名的函数:
可以通过函数重载来设计一系列函数——完成相同的工作,但使用不同的参数列表;
函数重载的关键是函数的参数列表——也称为函数特征标
需要注意,是特征标而不是函数类型使得可以对函数进行重载
函数重载例程:
1 #include<iostream> 2 using namespace std; 3 unsigned long left(unsigned long num, unsigned ct); // 处理整数 4 char * left(const char * str, int n = 1); // 处理字符串 5 6 int main() 7 { 8 char * trip = "Hawaii!! "; 9 unsigned long n = 12345678; 10 int i; 11 char *temp; 12 for (i = 1; i < 10; i++) 13 { 14 cout << left(n, i) << endl; 15 temp = left(trip, i); 16 cout << temp << endl; 17 delete[] temp; 18 } 19 return 0; 20 } 21 unsigned long left(unsigned long num, unsigned ct) 22 { 23 unsigned digits = 1; 24 unsigned long n = num; 25 26 if (ct == 0 || num == 0) 27 return 0; 28 while (n /= 10) 29 digits++; 30 if (digits > ct) 31 { 32 ct = digits - ct; 33 while (ct--) 34 num /= 10; 35 return num; 36 } 37 else 38 return num; 39 } 40 41 char * left(const char * str, int n) 42 { 43 if (n < 0) 44 n = 0; 45 char *p = new char[n + 1]; 46 int i; 47 for (i = 0; i < n && str[i]; i++) 48 p[i] = str[i]; 49 while (i <= n) 50 p[i++] = ‘\0‘; 51 return p; 52 53 }
仅当函数基本上执行相同的任务,但不使用不同形式的数据时,才应采用函数重载
函数模板是通用函数描述——使用通用类型来定义函数;
通过将类型作为参数传递给模板,可以使编译器生成该类型的函数;
建立交换模板:
template <class Any> // template <typename Any>
void Swap (Any &a,Any &b)
{
Any temp;
temp = a;
a = b;
b = temp;
}
重载的模板:
并非所有的类型都使用相同的算法,可以像重载常规函数定义那样重载模板定义;
被重载的模板的函数特征标必须不同;
template <class Any>
void Swap (Any &a,Any &b);
template <class Any>
void Swap (Any *a,Any *b,int n);
显示具体化:
可以提供一个具体化函数定义——称为显示具体化;
当编译器找到与函数调用匹配的具体化定义时,将使用该定义,而不再寻找模板;
C++标准选择的具体化方法:
1. 对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及他们的重载版本;
2. 显示具体化的原型和定义应以 template<> 打头,并通过名称来指出类型;
3. 具体化将覆盖常规模板,而非模板函数将覆盖具体化的原型
void Swap (job &,job &); // 非模板函数原型
template <class Any>
void Swap (Any &,Any &); // 模板函数原型
template <> void Swap<job> (job &,job &); // 具体化原型
实例化和具体化:
模板并非函数定义,编译器使用模板为特定类型生成函数定义时,得到的是模板实例:
函数调用 Swap(i,j) 导致编译器生成一个 Swap() 的一个实例——隐式实例
还可以直接命令编译器创建特定的实例,如 Swap<int>() ——显示化实例
temeplate void Swap<int> (int,int);
显示具体化使用下面两个等价声明之一:
template <> void Swap <int> (int &,int &);
template <> void Swap ( int &,int &);
原文:https://www.cnblogs.com/kidycharon/p/9705316.html