C++ Primer Plus 第8章 函数探幽

1.C++内联函数

 内联函数是C++为提高程序运行速度所做的一项改进。常规函数和内联函数之间的主要区别不在于编写方式,而在于C++编译器如何将他们组合到程序中。(编译过程的最终产品是可执行程序------由一组机器语言指令组成),如果代码执行时间短,则内联调用就可以节省非内敛调用使用的大部分时间。使用内联函数时:(1)在函数声明前加上关键字inline;(2)在函数定义前加上关键字inline。

inline工具是C++新增的特性。C语言使用预处理器语句#define来提供宏------内联代码的原始实现。

2.引用变量

C++新增了一种复合类型:引用变量。引用是已定义的变量的别名(主要用于函数的形参)。创建引用变量如下:

int rats;
int & rodents=rats;//rats和rodents指向相同的值和内存单元

      引用和指针的区别:

          (1)必须在声明引用变量时进行初始化。引用更接近const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠于它。

int & rodents=rats;//实际上是下面代码的伪装表示
int *const pr=&rats;

     如果只是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应使用常量引用(const....)

    左值参数是可被引用的数据对象,如:变量、数组元素、结构成员、引用和解除引用的指针都是左值。常规变量属于可以修改的左值,而const变量属于不可修改的左值。

     对于形参为const引用的C++函数,如果实参不匹配,则其行为类似于按值传递,为确保原始数据不被修改,将使用临时变量来存储值。

     C++11新增了另一种引用----右值引用,这种引用可指向右值,是使&&声明的。

     返回引用的函数实际上是被引用的变量的别名。返回引用时最重要的一点是,应避免返回函数终止时不再存在的内存单元引用。

    继承:能够将特性从一个类传递给另一个类的语言特性。如:ostream是基类,ofstream是派生类,派生类继承了基类的方法。继承的另一个特征是基类引用可以指向派生类对象,而无需进行强制类型转换。

#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 << "Eyepieces #" << i + 1 << ":";
		cin >> eps[i];

	}
	file_it(fout, objective, eps, LIMIT);//将目镜数据写入到文件ep_data.txt中
	file_it(cout, objective, eps, LIMIT);//将同样的信息以同样的格式显示到屏幕上
	cout << "Done\n";
	return 0;
}
void file_it(ostream &os, double fo, const double fe[], int n)
{//参数os(其类型为ostream &)可以指向ostream对象(如cout),也可以指向ofstream对象(如fout)
	ios_base::fmtflags initial;
	initial = os.setf(ios_base::fixed);//方法setf()让你能够设置各种格式化状态。   setf(ios_base::fixed)将对象置于使用定点表示法的模式
	os.precision(0);
	os << "Focal length of objective:" << fo << "mm\n";
	os.setf(ios::showpoint);//setf(ios::showpoint)将对象置于显示小数点的模式
	os.precision(1);//方法precision指定显示多少位小数
	os.width(12);//方法width设置下一次输出操作使用的字段宽度,这种设置只在显示下一个值时有效,然后将恢复到默认设置。默认的字段宽度为零,这意味着刚好能容纳下要显示的内容
	os << "f.1.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;
	}
	os.setf(initial);
}

   ios_base 是C++标准程序库中的一个类,定义于头文件中。ios_base类封装了C++标准中的流输入输出中不依赖于读写的数据的类型的基本信息,如格式化信息、异常状态、事件回调函数等。

     格式控制信息的枚举类型fmtflags ,影响到如何解释输入序列的格式、如何生成输出序列的格式,例如整数是16进制还是10进制表示,浮点数是科学计数法还是定点形式等;

     使用引用参数的两个主要原因:(1)能够修改调用函数中的数据对象;(2)通过传递引用而不是整个数据对象,可以提高程序的运行速度。

3.默认参数

       对于带参数列表的函数,必须从右向左添加默认值-------要为某个参数设置默认值,则必须为它右边的所有参数提供默认值。

4.函数模板

     函数模板允许以任意类型的方式来定义函数。

template<typename AnyType>//建立一个类模板,并将类型命名为AnyType
void Swap(AnyType &a, AnyType &b)
{
    AnyType temp;
    temp=a;
    a=b;
    b=temp;
}

      显示具体化:提供一个具体化函数定义

struct job
{
    char name[40];
    double salary;
    int floor;
};//如果只交换其中的部分成员,则需使用不同的代码。

//C++98标准选择了下面的方法实现具体化。
//1.对于给定的函数名,可以有非模板函数、模板函数和显示具体化模板函数以及他们的重载函数。
//2.显示具体化的原型和定义应以template<>打头,并通过名称来指出类型
//3.具体化优先于常规模板,而非模板函数优先于具体化和常规模板。

             重载解析:决定函数调用使用哪一个函数定义。

                  (1)完全匹配和最佳匹配:Type表示任意类型-----用作实参的函数名与用作形参的函数指针只要返回类型和参数列表相同,就是匹配的。指向非const数据的指针和引用优先与非const指针和引用参数匹配。

        如果两个完全匹配的函数都是模板函数,则较具体的模板函数优先。(显示具体化优先于隐式生成的具体化)。“最具体”并不意味着显式具体化,而是编译器推断使用哪种类型时执行的转换最少。

//两个模板
template <class Type> void recycle(Type t);// #1
template <class Type> void recycle(Type * t);// #2
//假设包含模板的程序如下:
strcut blot {int a; char b[10];};
blot ink={25,"spots"};
recycle(&ink);
//recycle(&ink)调用与#1模板匹配,匹配时将Type解释为blot*。recycle(&ink)函数调用也与#2模板匹配,这次Type被解释为ink,recycle<blot *>(blot *)和recycle<blot>(blot *)发送到可行函数池中,在这两个模板函数中,recycle<blot *>(blot *)被认为是更具体的,因为在生成过程中,它需要进行的转换更少。#2模板已经显示指出,函数参数是指向Type的指针,因此可以直接用blot表示Type;而#1模板将Type作为函数参数,因此Type必须被解释为指向blot的指针。也就是说,在#2模板中,Type已经被具体化为指针,因此她“更具体”。

          重载解析将寻找最匹配的函数。如果只存在一个这样的函数,则选择它;如果存在多个这样的函数,但其中只有一个是非模板函数,则选择该函数;如果存在多个合适的函数,且它们都为模板函数,但其中有一个函数比其他函数更具体,则选择该函数。如果有多个同样适合的非模板函数或模板函数,但没有一个函数比其他函数更具体,则函数调用将不确定,因此是错误的。

#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;
}
int main()
{
    using namespace std;
    int m=20;
    int n=-30;
    double x=15.5;
    double y=25.9;
    cout<<lesser(m,n)<<endl;
    cout<<lesser(x,y)<<endl;
    cout<<lesser<>(m,n)<<endl;//lesser<>(m,n)中<>指出,编译器应该选择模板函数,而不是非模板函数
    cout<<lesser<int>(x,y)<<endl;
    return 0;
}

           关键字decltype(C++11新增)

int x;
decltype(x) y;//make y the same type as x
decltype(x+y) xpy;//make xpy the same type as x+y

//decltype(expression) var;//核对表的简化版如下:
//第一步:如果expression是一个没有括号括起的标识符,则var的类型与该标识符的类型相同,包括const等限定符:
double x=5.5;
double y=7.9;
double &rx=x;
const double *pd;
decltype(x) w;//w is type double
decltype(rx) u=y;//u is type double&
decltype(pd) v;//v is type const double *
//第二步:如果expression是一个函数调用,则var的类型与函数的返回类型相同:
long indeed(int)
decltype (indeed(3)) m;//m is type int
//第三步:如果expression是一个左值,则var指向其类型的引用。要进入第三步,expression不能是未用括号括起的标识符。
double xx=4.4;
decltype ((xx)) r2=xx;//r2 is double &
decltype(xx) w=xx;//w is double(stage 1 match)
//第四步:如果前面的条件都不满足,则var的类型与expression的类型相同:
int j=3;
int &k=j;
int &n=j
decltype(j+6) i1;//i1 type int
decltype(100L) i2;//i2 type long
decltype(k+n) i3;//i3 type int



猜你喜欢

转载自blog.csdn.net/CHY1156957638/article/details/85675706