文章目录

  • ​​一、C++内联函数​​
  • ​​二、引用变量:C++新增​​
  • ​​2.将引用用作函数参数(引用用的最主要的方式)​​
  • ​​3.引用的属性和特别之处​​
  • ​​(f)再次强调一下使用const的好处:尽可能地将引用声明为const​​
  • ​​4.将引用用于结构​​
  • ​​5.将引用用于类对象​​
  • ​​6.对象、继承和引用​​
  • ​​7.何时使用引用参数??​​
  • ​​三、默认参数​​
  • ​​四、函数重载​​
  • ​​1.函数重载的例子​​
  • ​​2.何时使用函数重载​​
  • ​​五、函数模板​​
  • ​​1.函数模板的基本知识​​
  • ​​2.重载模板​​
  • ​​3.模板的局限性​​
  • ​​4.显示具体化​​
  • ​​5.实例化和具体化​​
  • ​​6.编译器选择使用哪个函数版本​​
  • ​​7.函数模板的发展(了解即可,decltype关键字实在看得太少了)​​
  • ​​六、总结​​

一、C++内联函数

(1)C++区别于C语言,提供了新特性:内联函数,引用传递变量,默认的参数值,函数重载(多态)以及模板函数;

(2)C++内联函数的优势:

《C++ primer plus》学习笔记——第八章函数探幽_#include


《C++ primer plus》学习笔记——第八章函数探幽_内联函数_02

(3)写内联函数的要求:
(a)在函数声明前加上关键字inline;
(b)在函数定义前加上关键字inline;
(c)内联函数不能递归

(4)什么时候用内联函数呢?

代码量不多,且经常使用

(5)eg如下:

《C++ primer plus》学习笔记——第八章函数探幽_#include_03

#include<stdio.h>
#include<stdlib.h>


// inline.cpp -- using an inline function
#include <iostream>
// an inline function definition
inline double square(double x) { return x * x; }
int main()
{
using namespace std;
double a, b;
double c = 13.0;

a = square(5.0);
b = square(4.5 + 7.5); // can pass expressions
cout << "a = " << a << ", b = " << b << "\n";
cout << "c = " << c;
cout << ", c squared = " << square(c++) << "\n";
cout << "Now c = " << c << "\n";

system("pause");
return 0;
}

《C++ primer plus》学习笔记——第八章函数探幽_ios_04

说明:

(1)

《C++ primer plus》学习笔记——第八章函数探幽_#include_05


(2)内联函数与宏定义的区别

《C++ primer plus》学习笔记——第八章函数探幽_#include_06

二、引用变量:C++新增

(1)引用变量的意思:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_07


(2)创建引用变量:

(a)C和C++使用&符号来指示变量的地址。C++给&符号赋予了另一个含义,将其用来声明引用。

(b)eg1:

《C++ primer plus》学习笔记——第八章函数探幽_#include_08


eg2:

#include<stdio.h>
#include<stdlib.h>


// firstref.cpp -- defining and using a reference
#include <iostream>
int main()
{
using namespace std;
int rats = 101;
int & rodents = rats; // rodents is a reference

cout << "rats = " << rats;
cout << ", rodents = " << rodents << endl;
rodents++;
cout << "rats = " << rats;
cout << ", rodents = " << rodents << endl;

// some implementations require type casting the following
// addresses to type unsigned
cout << "rats address = " << &rats;
cout << ", rodents address = " << &rodents << endl;

system("pause");
return 0;
}

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_09


说明:

(a) int & rodents = rats; // rodents is a reference

意思是:将rodents的类型声明为int &,即指向int变量的引用

(b) cout << ", rodents address = " << &rodents << endl;

意思是:&运算符是地址运算符,其中&rodents表示rodents引用的变量的地址

(c)引用和指针的区别如下:

《C++ primer plus》学习笔记——第八章函数探幽_#include_10


相似点:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_11

《C++ primer plus》学习笔记——第八章函数探幽_ios_12

不同点:引用还是不同于指针的!

第一点:

必须在声明引用时将其初始化,而不能像指针那样,先申明,再赋值。

《C++ primer plus》学习笔记——第八章函数探幽_ios_13


第二点:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_14

(3)eg:

《C++ primer plus》学习笔记——第八章函数探幽_ios_15

#include<stdio.h>
#include<stdlib.h>


// secref.cpp -- defining and using a reference
#include <iostream>
int main()
{
using namespace std;
int rats = 101;
int & rodents = rats; // rodents is a reference

cout << "rats = " << rats;
cout << ", rodents = " << rodents << endl;

cout << "rats address = " << &rats;
cout << ", rodents address = " << &rodents << endl;

int bunnies = 50;
rodents = bunnies; // can we change the reference?
cout << "bunnies = " << bunnies;
cout << ", rats = " << rats;
cout << ", rodents = " << rodents << endl;

cout << "bunnies address = " << &bunnies;
cout << ", rodents address = " << &rodents << endl;

system("pause");
return 0;
}

《C++ primer plus》学习笔记——第八章函数探幽_ios_16

说明:

(a)

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_17

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_18


(b)指针和引用的小eg

《C++ primer plus》学习笔记——第八章函数探幽_#include_19

2.将引用用作函数参数(引用用的最主要的方式)

(1)引用传递的含义以及对C语言的超越地方的说明

《C++ primer plus》学习笔记——第八章函数探幽_#include_20


(2)按值传递和按指针传递的区别:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_21

(3)交换两个变量的值,引用传递,使用指针和值传递之间的区别

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_22


《C++ primer plus》学习笔记——第八章函数探幽_ios_23

具体eg如下所示:

#include<stdio.h>
#include<stdlib.h>


// swaps.cpp -- swapping with references and with pointers
#include <iostream>
void swapr(int & a, int & b); // a, b are aliases for ints
void swapp(int * p, int * q); // p, q are addresses of ints
void swapv(int a, int b); // a, b are new variables
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); // pass variables
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;

cout << "Using pointers to swap contents again:\n";
swapp(&wallet1, &wallet2); // pass addresses of variables
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;

cout << "Trying to use passing by value:\n";
swapv(wallet1, wallet2); // pass values of variables
cout << "wallet1 = $" << wallet1;
cout << " wallet2 = $" << wallet2 << endl;

system("pause");
return 0;
}

void swapr(int & a, int & b) // use references
{
int temp;

temp = a; // use a, b for values of variables
a = b;
b = temp;
}

void swapp(int * p, int * q) // use pointers
{
int temp;

temp = *p; // use *p, *q for values of variables
*p = *q;
*q = temp;
}

void swapv(int a, int b) // try using values
{
int temp;

temp = a; // use a, b for values of variables
a = b;
b = temp;
}

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_24

说明:

(a)引用传递和指针都成功的交换了两个wallet的内容,而值传递的方法没能完成这项任务。

(b)引用传递与值传递的区别如下:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_25


(c)引用传递和指针传递的区别如下:

《C++ primer plus》学习笔记——第八章函数探幽_#include_26

3.引用的属性和特别之处

(1)eg

《C++ primer plus》学习笔记——第八章函数探幽_ios_27

#include<stdio.h>
#include<stdlib.h>


// cubes.cpp -- regular and reference arguments
#include <iostream>
double cube(double a);
double refcube(double &ra);
int main ()
{
using namespace std;
double x = 3.0;

cout << cube(x);
cout << " = cube of " << x << endl;
cout << refcube(x);
cout << " = cube of " << x << endl;
// cin.get();

system("pause");
return 0;
}


double cube(double a)
{
a *= a * a;
return a;
}

double refcube(double &ra)
{
ra *= ra * ra;
return ra;
}

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_28


说明:

(a)

《C++ primer plus》学习笔记——第八章函数探幽_ios_29

(b)啥时候用值传递,啥时候用指针传递?

《C++ primer plus》学习笔记——第八章函数探幽_ios_30


(c)传递引用限制更加严格

《C++ primer plus》学习笔记——第八章函数探幽_ios_31


(d)如果写成如下的形式,注意体会在形参中使用const的感觉

#include<iostream>
#include<stdlib.h>
#include<stdio.h>


// cubes.cpp -- regular and reference arguments
#include <iostream>
double cube(double a);
double refcube(const double &ra);
int main()
{
using namespace std;
double x = 3.0;
//double re;


cout << cube(x);
cout << " = cube of " << x << endl;
double re=refcube(x);
cout << re;

cout << " = cube of " <<x<< endl;
// cin.get();
cout<<"shazi"<<endl;

//system("pause");
return 0;
}


double cube(double a)
{
a *= a * a;
return a;
}

double refcube(const double &ra)
{
double res;
res=ra* ra * ra;
return res;
}

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_32

(2)临时变量、引用、const

(a)如果实参与引用参数不匹配,C++将生成临时变量

(b)什么时候创建临时变量呢?

《C++ primer plus》学习笔记——第八章函数探幽_ios_33


(c)什么是左值,非左值?

《C++ primer plus》学习笔记——第八章函数探幽_ios_34


(d)eg:

《C++ primer plus》学习笔记——第八章函数探幽_#include_35


《C++ primer plus》学习笔记——第八章函数探幽_内联函数_36


说明:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_37

(e)

《C++ primer plus》学习笔记——第八章函数探幽_ios_38

(f)再次强调一下使用const的好处:尽可能地将引用声明为const

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_39

4.将引用用于结构

(1)使用引用主要是为了用于结构和类这些类型的,而不是基本的内置类型。

(2)在结构中如何使用引用?

《C++ primer plus》学习笔记——第八章函数探幽_#include_40


eg1:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_41

eg2:

#include<stdio.h>
#include<stdlib.h>


//strc_ref.cpp -- using structure references
#include <iostream>
#include <string>
struct free_throws
{
std::string name;
int made;
int attempts;
float percent;
};

void display(const free_throws & ft);
void set_pc(free_throws & ft);
free_throws & accumulate(free_throws &target, const free_throws &source);

int main()
{
free_throws one = {"Ifelsa Branch", 13, 14};
free_throws two = {"Andor Knott", 10, 16};
free_throws three = {"Minnie Max", 7, 9};
free_throws four = {"Whily Looper", 5, 9};
free_throws five = {"Long Long", 6, 14};
free_throws team = {"Throwgoods", 0, 0};
free_throws dup;
set_pc(one);
display(one);
accumulate(team, one);
display(team);
// use return value as argument
display(accumulate(team, two));
accumulate(accumulate(team, three), four);
display(team);
// use return value in assignment
dup = accumulate(team,five);
std::cout << "Displaying team:\n";
display(team);
std::cout << "Displaying dup after assignment:\n";
display(dup);
set_pc(four);
// ill-advised assignment
accumulate(dup,five) = four;
std::cout << "Displaying dup after ill-advised assignment:\n";
display(dup);
// std::cin.get();

system("pause");
return 0;
}



void display(const free_throws & ft)
{
using std::cout;
cout << "Name: " << ft.name << '\n';
cout << " Made: " << ft.made << '\t';
cout << "Attempts: " << ft.attempts << '\t';
cout << "Percent: " << ft.percent << '\n';
}
void set_pc(free_throws & ft)
{
if (ft.attempts != 0)
ft.percent = 100.0f *float(ft.made)/float(ft.attempts);
else
ft.percent = 0;
}

free_throws & accumulate(free_throws & target, const free_throws & source)
{
target.attempts += source.attempts;
target.made += source.made;
set_pc(target);
return target;
}

《C++ primer plus》学习笔记——第八章函数探幽_ios_42

说明:

(a)在初始化多个结构对象时,如果指定的初始值比成员少,则余下的成员(这里的percent)将被设置为0.

(b)对于set_pc()函数而言

《C++ primer plus》学习笔记——第八章函数探幽_#include_43

《C++ primer plus》学习笔记——第八章函数探幽_#include_44


(c)对于display()函数而言

《C++ primer plus》学习笔记——第八章函数探幽_ios_45


(d)对于accumulate()函数而言

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_46


(e)对于返回值是引用而言的说明如下,

《C++ primer plus》学习笔记——第八章函数探幽_ios_47


具体意思就是:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_48


《C++ primer plus》学习笔记——第八章函数探幽_ios_49


(f)为何要返回引用??

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_50


(j)返回引用时需要注意的问题

错误的示例:返回临时变量

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_51


正确的方法1

《C++ primer plus》学习笔记——第八章函数探幽_ios_52

正确的方法2

《C++ primer plus》学习笔记——第八章函数探幽_#include_53


《C++ primer plus》学习笔记——第八章函数探幽_ios_54


(h)为何将const用于引用返回类型?

第一点:

《C++ primer plus》学习笔记——第八章函数探幽_#include_55


第二点:

《C++ primer plus》学习笔记——第八章函数探幽_ios_56

5.将引用用于类对象

(1)通过使用引用,可以让函数将类string、ostream、istream、ofstream和ifstream等类的对象作为参数。
(2)eg的基本思想是:使用string类,创建一个函数,它将指定的字符串加入到另一个字符串的前面和后面。

#include<stdio.h>
#include<stdlib.h>


// strquote.cpp -- different designs
#include <iostream>
#include <string>
using namespace std;
string version1(const string & s1, const string & s2);
const string & version2(string & s1, const string & s2); // has side effect
const string & version3(string & s1, const string & s2); // bad design

int main()
{
string input;
string copy;
string result;

cout << "Enter a string: ";
getline(cin, input);
copy = input;
cout << "Your string as entered: " << input << endl;
result = version1(input, "***");
cout << "Your string enhanced: " << result << endl;
cout << "Your original string: " << input << endl;

result = version2(input, "###");
cout << "Your string enhanced: " << result << endl;
cout << "Your original string: " << input << endl;

cout << "Resetting original string.\n";
input = copy;
result = version3(input, "@@@");
cout << "Your string enhanced: " << result << endl;
cout << "Your original string: " << input << endl;

system("pause");
return 0;
}



string version1(const string & s1, const string & s2)
{
string temp;

temp = s2 + s1 + s2;
return temp;
}

const string & version2(string & s1, const string & s2) // has side effect
{
s1 = s2 + s1 + s2;
// safe to return reference passed to function
return s1;
}

const string & version3(string & s1, const string & s2) // bad design
{
string temp;

temp = s2 + s1 + s2;
// unsafe to return reference to local variable
return temp;
}

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_57


说明:

(a)将C语言风格的字符串用作string对象引用参数的说明

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_58


《C++ primer plus》学习笔记——第八章函数探幽_内联函数_59

(b)对于第一个version1而言,

《C++ primer plus》学习笔记——第八章函数探幽_#include_60


(c)对于第二个version2而言,

《C++ primer plus》学习笔记——第八章函数探幽_ios_61


《C++ primer plus》学习笔记——第八章函数探幽_内联函数_62

(d)对于第三个version3而言,不要对临时变量返回引用

《C++ primer plus》学习笔记——第八章函数探幽_ios_63

6.对象、继承和引用

(1)ofstream对象可以使用ostream类的方法,这使得文件输入/输出的格式与控制台输入/输出相同

(2)继承的特征1:

继承:使得能够将特性从一个类传递给另一个类的语言特性。

eg:

《C++ primer plus》学习笔记——第八章函数探幽_#include_64


(3)继承的特征2:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_65

《C++ primer plus》学习笔记——第八章函数探幽_#include_66

(4)eg:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_67

#include<stdio.h>
#include<stdlib.h>


//filefunc.cpp -- function with ostream & parameter
#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;

void file_it(ostream & os, double fo, const double fe[],int n);
const int LIMIT = 5;
int main()
{
ofstream fout;
const char * fn = "ep-data.txt";
fout.open(fn);
if (!fout.is_open())
{
cout << "Can't open " << fn << ". Bye.\n";
exit(EXIT_FAILURE);
}
double objective;
cout << "Enter the focal length of your "
"telescope objective in mm: ";
cin >> objective;
double eps[LIMIT];
cout << "Enter the focal lengths, in mm, of " << LIMIT
<< " eyepieces:\n";
for (int i = 0; i < LIMIT; i++)
{
cout << "Eyepiece #" << i + 1 << ": ";
cin >> eps[i];
}
file_it(fout, objective, eps, LIMIT);
file_it(cout, objective, eps, LIMIT);
cout << "Done\n";

system("pause");
return 0;
}



void file_it(ostream & os, double fo, const double fe[],int n)
{
// save initial formatting state
ios_base::fmtflags initial;
initial = os.setf(ios_base::fixed, ios_base::floatfield);
std::streamsize sz = os.precision(0);
os << "Focal length of objective: " << fo << " mm\n";
os.precision(1);
os.width(12);//可以不要
os << "f.l. eyepiece";
os.width(15);//可以不要
os << "magnification" << endl;
for (int i = 0; i < n; i++)
{
os.width(12);
os << fe[i];
os.width(15);
os << int (fo/fe[i] + 0.5) << endl;
}
// restore initial formatting state
os.setf(initial, ios_base::floatfield);
os.precision(sz);
}

《C++ primer plus》学习笔记——第八章函数探幽_ios_68


说明:

(a)参数os(类型是ostream &)可以指向ostream对象(如cout),也可以指向ofstream对象(如fout)。

(b)使用ostream类中的格式化方法

setf()能够设置各种格式化状态:

setf(ios_base::fixed)将对象置于使用定点表示;

setf(ios_base::showpoint)将对象置于显示小数点的模式,即小数部分为0;

precision()指定显示多少位小数(假设对象处于定点模式下);

width()设置下一次输出操作使用的字段宽度,这种设置只在显示下一个值时有效,然后就恢复到默认设置(默认为0);

所有这些设置都将一直保持不变,直到再次调用相应的方法重新设置它们(c)

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_69


《C++ primer plus》学习笔记——第八章函数探幽_ios_70

7.何时使用引用参数??

《C++ primer plus》学习笔记——第八章函数探幽_#include_71

三、默认参数

(1)默认参数指的是:当函数调用中省略了实参时,自动使用的一个值。

(2)如何设置默认值??

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_72


(3)对于带参数列表的函数而言

《C++ primer plus》学习笔记——第八章函数探幽_#include_73


(4)对于实参而言

《C++ primer plus》学习笔记——第八章函数探幽_ios_74


(5)默认参数的好处

《C++ primer plus》学习笔记——第八章函数探幽_ios_75

(6)具体eg:

《C++ primer plus》学习笔记——第八章函数探幽_#include_76

#include<stdio.h>
#include<stdlib.h>


// left.cpp -- string function with a default argument
#include <iostream>
const int ArSize = 80;
char * left(const char * str, int n = 1);
int main()
{
using namespace std;
char sample[ArSize];
cout << "Enter a string:\n";
cin.get(sample,ArSize);
char *ps = left(sample, 4);
cout << ps << endl;
delete [] ps; // free old string
ps = left(sample);
cout << ps << endl;
delete [] ps; // free new string

system("pause");
return 0;
}


// This function returns a pointer to a new string
// consisting of the first n characters in the str string.
char * left(const char * str, int n)
{
if(n < 0)
n = 0;
char * p = new char[n+1];
int i;
for (i = 0; i < n && str[i]; i++)
p[i] = str[i]; // copy characters
while (i <= n)
p[i++] = '\0'; // set rest of string to '\0'
return p;
}

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_77

说明:

(a)第一次用的是实参的参数,第二次使用的是默认参数,而默认参数在函数声明那里

《C++ primer plus》学习笔记——第八章函数探幽_ios_78


(b)在函数char * left()中,很细节的考虑到了三个地方,并且用while循环来给新的字符串加结尾\0

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_79


(c)更进一步地,可以设置一种新字符串长度

《C++ primer plus》学习笔记——第八章函数探幽_ios_80

(d)但是作为C语言程序员以及C++程序员,还可以做的更好,C程序员用strlen,C++程序员就会用比strlen更好的方法。

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_81

四、函数重载

(1)默认参数与函数多态(函数重载)的区别是:
默认参数让您能够使用不同数目的参数调用同一个函数;
**函数多态(函数重载)**让您能够使用多个同名的函数,即完成相同的工作,但是使用不同的参数列表。

(2)函数重载的关键是函数的参数列表——也称之为函数特征标(function signature)。C++允许定义名称相同的函数,条件是:它们的特征标不同,如果参数数目和或者参数类型不同,则特征标也不同。

(3)eg:

《C++ primer plus》学习笔记——第八章函数探幽_ios_82


(4)有些不同的特征标是不能共存的:引用和类型本身视为同一个特征标

《C++ primer plus》学习笔记——第八章函数探幽_ios_83

(5)

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_84


说明:

(a)dribble()函数有两个原型,一个用于const指针,另一个用于常规指针。

dribble()函数只与带非const参数的调用匹配;

drivel()函数可以与带const或者不带const参数的调用匹配

《C++ primer plus》学习笔记——第八章函数探幽_ios_85

(6)

《C++ primer plus》学习笔记——第八章函数探幽_#include_86

(7)对于重载引用参数而言,类设计和STL会经常用到

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_87

1.函数重载的例子

(1)前面写了一个left()函数,它返回了一个指针,指向字符串的前n个字符。
在这里,再添加另一个left()函数,让其返回整数的前n位。

(2)编写的要点:

首先,先计算数字包含多少位。将数字除以10便可以去掉一位,因此,可以使用出发来计算数位

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_88


《C++ primer plus》学习笔记——第八章函数探幽_内联函数_89


其次,是要删除多少位

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_90

(3)最终,需要编写的函数的要求:由于考虑的多,所以最终的结果很完美

《C++ primer plus》学习笔记——第八章函数探幽_ios_91

#include<stdio.h>
#include<stdlib.h>


// leftover.cpp -- overloading the left() function
#include <iostream>
unsigned long left(unsigned long num, unsigned ct);
char * left(const char * str, int n = 1);

int main()
{
using namespace std;
char * trip = "Hawaii!!"; // test value
unsigned long n = 12345678; // test value
int i;
char * temp;

for (i = 1; i < 10; i++)
{
cout << left(n, i) << endl;
temp = left(trip,i);
cout << temp << endl;
delete [] temp; // point to temporary storage
}

system("pause");
return 0;
}


// This function returns the first ct digits of the number num.
unsigned long left(unsigned long num, unsigned ct)
{
unsigned digits = 1;
unsigned long n = num;

if (ct == 0 || num == 0)
return 0; // return 0 if no digits
while (n /= 10)
digits++;//总共有多少位digits
if (digits > ct)
{
ct = digits - ct;
while (ct--)
num /= 10;
return num; // return left ct digits
}
else // if ct >= number of digits
return num; // return the whole number
}

// This function returns a pointer to a new string
// consisting of the first n characters in the str string.
char * left(const char * str, int n)
{
if(n < 0)
n = 0;
char * p = new char[n+1];
int i;
for (i = 0; i < n && str[i]; i++)
p[i] = str[i]; // copy characters
while (i <= n)
p[i++] = '\0'; // set rest of string to '\0'
return p;
}

《C++ primer plus》学习笔记——第八章函数探幽_ios_92

2.何时使用函数重载

(1)

《C++ primer plus》学习笔记——第八章函数探幽_ios_93


(2)

《C++ primer plus》学习笔记——第八章函数探幽_#include_94

五、函数模板

1.函数模板的基本知识

(1)C++新增的一项特性——函数模板
使用泛型来定义函数,其中的泛型可用具体的类型(如int或double)替换。
通过将类型作为参数传递给模板,可使编译器生成该类型的函数
由于模板允许以泛型(不是具体类型) 的方式编写程序,因此有时候也被称之为:通用编程。

(2)C++模板的到底是干啥的??

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_95

(3)举例一个函数模板的例子eg如下:

《C++ primer plus》学习笔记——第八章函数探幽_ios_96


说明:

(a)

《C++ primer plus》学习笔记——第八章函数探幽_ios_97


(b)使用模板的作用是:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_98

(4)再举例一个函数模板的例子eg如下:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_99


《C++ primer plus》学习笔记——第八章函数探幽_内联函数_100

(5)函数模板在哪用?

《C++ primer plus》学习笔记——第八章函数探幽_#include_101

(6)举个eg:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_102

#include<stdio.h>
#include<stdlib.h>


// funtemp.cpp -- using a function template
#include <iostream>
// function template prototype
template <typename T> // or class T
void Swap(T &a, T &b);

int main()
{
using namespace std;
int i = 10;
int j = 20;
cout << "i, j = " << i << ", " << j << ".\n";
cout << "Using compiler-generated int swapper:\n";
Swap(i,j); // generates void Swap(int &, int &)
cout << "Now i, j = " << i << ", " << j << ".\n";

double x = 24.5;
double y = 81.7;
cout << "x, y = " << x << ", " << y << ".\n";
cout << "Using compiler-generated double swapper:\n";
Swap(x,y); // generates void Swap(double &, double &)
cout << "Now x, y = " << x << ", " << y << ".\n";
// cin.get();

system("pause");
return 0;
}




// function template definition
template <typename T> // or class T
void Swap(T &a, T &b)
{
T temp; // temp a variable of type T
temp = a;
a = b;
b = temp;
}

《C++ primer plus》学习笔记——第八章函数探幽_#include_103

说明:

(a)用int替换了所有的T

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_104


(b)同理,用double替换了T

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_105

(3)使用模板的好处:使得生成的多个函数定义更简单、更可靠。
更常见的情况是,将模板放在头文件中,并在需要使用 模板的文件中包含头文件。

2.重载模板

(1)重载模板的特点:

由于并非所有的类型都使用相同的算法(函数模板的特点),可以像重载函数那样定义重载模板。

要求:被重载模板的函数特征标必须不同。

eg下面的例子:

特点:

《C++ primer plus》学习笔记——第八章函数探幽_ios_106

#include<stdio.h>
#include<stdlib.h>


// twotemps.cpp -- using overloaded template functions
#include <iostream>
template <typename T> // original template
void Swap(T &a, T &b);

template <typename T> // new template
void Swap(T *a, T *b, int n);

void Show(int a[]);
const int Lim = 8;
int main()
{
using namespace std;
int i = 10, j = 20;
cout << "i, j = " << i << ", " << j << ".\n";
cout << "Using compiler-generated int swapper:\n";
Swap(i,j); // matches original template
cout << "Now i, j = " << i << ", " << j << ".\n";

int d1[Lim] = {0,7,0,4,1,7,7,6};
int d2[Lim] = {0,7,2,0,1,9,6,9};
cout << "Original arrays:\n";
Show(d1);
Show(d2);
Swap(d1,d2,Lim); // matches new template
cout << "Swapped arrays:\n";
Show(d1);
Show(d2);
// cin.get();

system("pause");
return 0;
}


template <typename T>
void Swap(T &a, T &b)
{
T temp;
temp = a;
a = b;
b = temp;
}

template <typename T>
void Swap(T a[], T b[], int n)
{
T temp;
for (int i = 0; i < n; i++)
{
temp = a[i];
a[i] = b[i];
b[i] = temp;
}
}

void Show(int a[])
{
using namespace std;
cout << a[0] << a[1] << "/";
cout << a[2] << a[3] << "/";
for (int i = 4; i < Lim; i++)
cout << a[i];
cout << endl;
}

《C++ primer plus》学习笔记——第八章函数探幽_ios_107

3.模板的局限性

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_108

4.显示具体化

(1)显示具体化解决了上述所谓的模板局限性

《C++ primer plus》学习笔记——第八章函数探幽_#include_109

(2)重载模板的匹配顺序

《C++ primer plus》学习笔记——第八章函数探幽_ios_110


eg:

《C++ primer plus》学习笔记——第八章函数探幽_#include_111


《C++ primer plus》学习笔记——第八章函数探幽_ios_112

(3)举一个显示具体化的eg,如下:

#include<stdio.h>
#include<stdlib.h>


// twoswap.cpp -- specialization overrides a template
#include <iostream>
template <typename T>
void Swap(T &a, T &b);

struct job
{
char name[40];
double salary;
int floor;
};

// explicit specialization
template <> void Swap<job>(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, j = 20;
cout << "i, j = " << i << ", " << j << ".\n";
cout << "Using compiler-generated int swapper:\n";
Swap(i,j); // generates void Swap(int &, int &)
cout << "Now i, j = " << i << ", " << j << ".\n";

job sue = {"Susan Yaffee", 73000.60, 7};
job sidney = {"Sidney Taffee", 78060.72, 9};
cout << "Before job swapping:\n";
Show(sue);
Show(sidney);
Swap(sue, sidney); // uses void Swap(job &, job &)
cout << "After job swapping:\n";
Show(sue);
Show(sidney);

system("pause");
return 0;
}


template <typename T>
void Swap(T &a, T &b) // general version
{
T temp;
temp = a;
a = b;
b = temp;
}

// swaps just the salary and floor fields of a job structure

template <> void Swap<job>(job &j1, job &j2) // specialization
{
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;
}

《C++ primer plus》学习笔记——第八章函数探幽_#include_113


说明:

(a)cout中对精度的控制怎么写??

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_114


(b)Swap(i,j)匹配的是

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_115


Swap(sue,sidney)匹配的是

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_116


(c)这个模板具体化

《C++ primer plus》学习笔记——第八章函数探幽_#include_117


(d)show()函数的形参中,定义了一个引用

《C++ primer plus》学习笔记——第八章函数探幽_#include_118

5.实例化和具体化

(1)显示实例化和隐式实例化

(a)我们知道,编译器使用模板为特定类型生成函数定义时,得到的是模板实例(instantiation)

隐式实例化

《C++ primer plus》学习笔记——第八章函数探幽_#include_119


eg:

template

void Swap(T &a, T &b) // general version

{

T temp;

temp = a;

a = b;

b = temp;

}显式实例化

《C++ primer plus》学习笔记——第八章函数探幽_#include_120


(b)显示具体化

《C++ primer plus》学习笔记——第八章函数探幽_#include_121

(c)显示具体化和显示实例化的区别:

《C++ primer plus》学习笔记——第八章函数探幽_#include_122


注意:

《C++ primer plus》学习笔记——第八章函数探幽_ios_123

(2)

《C++ primer plus》学习笔记——第八章函数探幽_ios_124


eg:对于其它的Swap()调用,编译器将根据函数调用时,实际使用的参数生成相应的版本。

《C++ primer plus》学习笔记——第八章函数探幽_#include_125

6.编译器选择使用哪个函数版本

(1)重载解析及其过程

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_126

(2)理解重载解析的很重要的例子

eg:考虑只有一个函数参数的情况,如下面的调用:

《C++ primer plus》学习笔记——第八章函数探幽_ios_127


首先编译器将寻找候选者, 即名称为may()的函数和函数模板(注意:只考虑特征标,不考虑返回类型)。然后寻找那些可以用一个参数调用的函数。

eg:

《C++ primer plus》学习笔记——第八章函数探幽_ios_128


《C++ primer plus》学习笔记——第八章函数探幽_ios_129


说明:把字符‘a’当作整型,

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_130


接下来,

《C++ primer plus》学习笔记——第八章函数探幽_#include_131


结果是:

函数3,函数5>函数6>函数1>函数2

《C++ primer plus》学习笔记——第八章函数探幽_ios_132

(3)完全匹配和最佳匹配

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_133


eg:

《C++ primer plus》学习笔记——第八章函数探幽_ios_134


说明:

(a)情况1:不能完全匹配,编译器可能会提示的错误是:

《C++ primer plus》学习笔记——第八章函数探幽_#include_135


(b)情况2:

《C++ primer plus》学习笔记——第八章函数探幽_#include_136


(c)情况3:

《C++ primer plus》学习笔记——第八章函数探幽_#include_137


eg1:

《C++ primer plus》学习笔记——第八章函数探幽_#include_138

eg2:最具体的含义

用于找出最具体的模板的规则被称之为:函数模板的部分排序规则

《C++ primer plus》学习笔记——第八章函数探幽_#include_139

(4)部分排序规则的eg如下:

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_140

#include<stdio.h>
#include<stdlib.h>


// tempover.cpp --- template overloading
#include <iostream>

template <typename T> // template A
void ShowArray(T arr[], int n);

template <typename T> // template B
void ShowArray(T * arr[], int n);

struct debts
{
char name[50];
double amount;
};

int main()
{
using namespace std;
int things[6] = {13, 31, 103, 301, 310, 130};

//结构体数组
struct debts mr_E[3] =
{
{"Ima Wolfe", 2400.0},
{"Ura Foxe", 1300.0},
{"Iby Stout", 1800.0}
};

//指针数组
double * pd[3];

// set pointers to the amount members of the structures in mr_E
for (int i = 0; i < 3; i++)
pd[i] = &mr_E[i].amount;

cout << "Listing Mr. E's counts of things:\n";
// things is an array of int
ShowArray(things, 6); // uses template A
cout << "Listing Mr. E's debts:\n";
// pd is an array of pointers to double
ShowArray(pd, 3); // uses template B (more specialized)
// cin.get();

system("pause");
return 0;
}



template <typename T>
void ShowArray(T arr[], int n)
{
using namespace std;
cout << "template A\n";
for (int i = 0; i < n; i++)
cout << arr[i] << ' ';
cout << endl;
}

template <typename T>
void ShowArray(T * arr[], int n)
{
using namespace std;
cout << "template B\n";
for (int i = 0; i < n; i++)
cout << *arr[i] << ' ';
cout << endl;
}

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_141


说明:

(a) ShowArray(things, 6); // uses template A

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_142


(b)ShowArray(pd, 3); // uses template B (more specialized)

与模板A匹配的话,输出地址的原因是cout<<arr[i]<< ’ ’

《C++ primer plus》学习笔记——第八章函数探幽_ios_143

与模板B匹配的话

《C++ primer plus》学习笔记——第八章函数探幽_#include_144

(c)

《C++ primer plus》学习笔记——第八章函数探幽_ios_145


《C++ primer plus》学习笔记——第八章函数探幽_ios_146

(5)自己选择

《C++ primer plus》学习笔记——第八章函数探幽_#include_147

#include<stdio.h>
#include<stdlib.h>


// choices.cpp -- choosing a template
#include <iostream>

template<class T>
T lesser(T a, T b) // #1
{
return a < b ? a : b;
}

int lesser (int a, int b) // #2
{
a = a < 0 ? -a : a;
b = b < 0 ? -b : b;
return a < b ? a : b;
}

int main()
{
using namespace std;
int m = 20;
int n = -30;
double x = 15.5;
double y = 25.9;

cout << lesser(m, n) << endl; // use #2
cout << lesser(x, y) << endl; // use #1 with double
cout << lesser<>(m, n) << endl; // use #1 with int
cout << lesser<int>(x, y) << endl; // use #1 with int

// cin.get();

system("pause");
return 0;
}

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_148


说明:

(a)先看lesser(m, n)和lesser(x, y) 的情况

《C++ primer plus》学习笔记——第八章函数探幽_内联函数_149


(b)再看lesser<>(m, n) 和 lesser< int >(x, y) 的情况

《C++ primer plus》学习笔记——第八章函数探幽_#include_150

(6)多个参数的函数

《C++ primer plus》学习笔记——第八章函数探幽_ios_151

7.函数模板的发展(了解即可,decltype关键字实在看得太少了)

(1)是什么类型??

《C++ primer plus》学习笔记——第八章函数探幽_ios_152


(2)C++11的关键字decltype

《C++ primer plus》学习笔记——第八章函数探幽_ios_153


《C++ primer plus》学习笔记——第八章函数探幽_ios_154


《C++ primer plus》学习笔记——第八章函数探幽_内联函数_155


(3)另一种函数声明语法(C++11后置返回类型

《C++ primer plus》学习笔记——第八章函数探幽_ios_156


《C++ primer plus》学习笔记——第八章函数探幽_内联函数_157

六、总结

《C++ primer plus》学习笔记——第八章函数探幽_#include_158

《C++ primer plus》学习笔记——第八章函数探幽_#include_159