|
前言
个人觉得学习编程最有效的方法是阅读专业的书籍,通过阅读专业书籍可以构建更加系统化的知识体系。 一直以来都很想深入学习一下C++,将其作为自己的主力开发语言。现在为了完成自己这一直以来的心愿,准备认真学习《C++ Primer Plus》。 为了提高学习效率,在学习的过程中将通过发布学习笔记的方式,持续记录自己学习C++的过程。
一、C++内联函数
内联函数是C++为提高程序运行速度所做的一项改进。常规函数和内联函数之间的主要区别不在于编写方式,而在于C++编译器如何将它们组合到程序中。
对于内联代码,程序无需跳到另一个位置处执行代码,再跳回来。因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存。如果程序在10个不同的地方调用同一个内联函数,则该程序将包含该函数代码的10个副本。
要使用这项特性,必须采取下述措施之一: + 在函数声明前加上关键字inline; + 在函数定义前加上关键字inline。 通常的做法是省略原型,将整个定义(即函数头和所有函数代码)放在本应提供原型的地方
二、引用变量
C++新增了一种复合类型——引用变量。引用是已定义的变量的别名(另一个名称)。
C和C++使用&符号来指示变量的地址。C++给&符号赋予了另一个含义,将其用来声明引用。例如,要将rodents作为rats变量的别名,可以这样做:
int rats;
int & rodents = rats;
int &指的是指向int的引用。
引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名。这种传递参数的方法称为按引用传递。按引用传递允许被调用的函数能够访问调用函数中的变量。
如果意图是让函数使用传递给它的信息,而不对这些信息进行修改,同时又想使用引用,则应使用常量引用:
double refcube(const double &ra);
如果实参与引用参数不匹配,C++将生成临时变量。当前,仅当参数为const引用时,C++才允许这样做,但以前不是这样。
如果引用参数是const,则编译器将在下面两种情况下生成临时变量: + 实参的类型正确,但不是左值; + 实参的类型不正确,但可以转换为正确的类型。
左值参数:是可被引用的数据对象,例如,变量、数组元素、结构成员、引用和解除引用的指针都是左值。
非左值:包括字面常量(用引号括起的字符串除外,它们由其地址表示)和包含多项的表达式。在C语言中,左值最初指的是可出现在赋值语句左边的实体,但这是引入关键字const之前的况。现在,常规变量和const变量都可视为左值,因为可通过地址访问它们。但常规变量属于可修改的左值,而const变量属于不可修改的左值。
将引用参教声明为常量数据的引用的理由有三个: + 使用const可以避免无意中修改数据的编程错误; + 使用const使函数能够处理const和非const实参,否则将只能接受非const数据; + 使用const引用使函数能够正确生成并使用临时变量。
C++11新增了另一种引用——右值引用(rvalue reference)。这种引用可指向右值,是使用&&声明的:
double && rref = std::sqrt(36.00);
新增右值引用的主要目的是,让库设计人员能够提供有些操作的更有效实现。
引用非常适合用于结构和类(C++的用户定义类型)。
返回引用的函数实际上是被引用的变量的别名。
返回引用时最重要的一点是,应避免返回函数终止时不再存在的内存单元引用。为避免这种问题,最简单的方法是,返回一个作为参数传递给函数的引用。另一种方法是用new来分配新的存储空间。
使得能够将特性从一个类传递给另一个类的语言特性被称为继承。
继承的另一个特征是,基类引用可以指向派生类对象,而无需进行强制类型转换。
使用引用参数的主要原因有两个: + 程序员能够修改调用函数中的数据对象。 + 通过传递引用而不是整个数据对象,可以提高程序的运行速度。
对于使用传递的值而不作修改的函数。 + 如果数据对象很小,如内置数据类型或小型结构,则按值传递。 + 如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明指向const的指针。 + 如果数据对象是较大的结构,则使用const指针或const引用,以提高程序的效率。这样可以节省复制结构所需的时间和空间。 + 如果数据对象是类对象,则使用const引用。类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因。因此,传递类对象参数的标准方式是按引用传递。
对于修改调用函数中数据的函数: + 如果数据对象是内置数据类型,则使用指针。如果看到诸如txi(&x)这样的代码(其中×尼imnt)则很明显,该函数将修改x。 + 如果数据对象是数组,则只能使用指针。 + 如果数据对象是结构,则使用引用或指针。 + 如果数据对象是类对象,则使用引用。 当然,这只是一些指导原则,很可能有充分的理由做出其他的选择。
三、默认参数
默认参数指的是当函数调用中省略了实参时自动使用的一个值。
char * left(const char * str, int n = 1);
对于带参数列表的函数,必须从右向左添加默认值。也就是说,要为某个参数设置默认值,则必须为它右边的所有参数提供默认值。
四、函数重载
函数多态是C++在C语言的基础上新增的功能。术语“多态”指的是有多种形式,因此函数多态允许函数可以有多种形式。类似地,术语“函数重载"’指的是可以有多个同名的函数,因此对名称进行了重载。这两个术语指的是同一回事,但我们通常使用函数重载。
函数重载的关键是函数的参数列表——也称为函数特征标(function signature)。编译器在检查函数特征标时,将把类型引用和类型本身视为同一个特征标。
五、函数模板
函数模板是通用的函数描述,也就是说,它们使用泛型来定义函数,其中的泛型可用具体地类型(如int 或 double)替换。由于类型是用参数表示的,因此模板特性有时也被称为参数化类型。
template <typename AnyType>
void Swap(AnyType &a, AnyType &b)
{
AnyType temp;
temp = a;
a = b;
b = temp;
}
关键字template和typename是必需的,除非可用使用关键字class代替typename。另外,必须使用尖括号。
在标准C++98添加关键字typename之前,C++使用关键字class来创建模板。也就是说,可以这样编写模板定义:
template <class AnyType>
void Swap(AnyType &a, AnyType &b)
{
AnyType temp;
temp = a;
a = b;
b = temp;
}可以提供一个具体化函数定义——称为显式具体化(explicit specialization),其中包含所需的代码。
C++98标准选择了下面的具体化方法: + 对于给定的函数名,可以有非模板函数、模板函数和显式具体化模板函数以及它们的重载版本。 + 显示具体化的原型和定义应以template<>打头,并通过名称来指出类型。 + 具体化优先于常规模板,而非模板函数优先于具体化和常规模板。
template<> void Swap(job & , job &);
为进一步了解模板,必须理解术语实例化和具体化。记住,在代码中包合函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案。编译器使用模板为特定类型生成明数定义时,得到的是模板实例(instantiation)。这种实例化方式被称为隐式实例化(implicit instantiation)。
现在C++还允许显式实例化(explicit instantiation):
template void Swap<int>(int, int);
与显式实例化不同的是,显式具体化使用下面两个等价的声明之一:
template <> void Swap<int>(int &, int &);
template <> void Swap(int &, int &);
区别在于,这些声明的意思是“不要使用Swap()模板来生成函数定义,而应使用专门为int类型显式地定义的函数定义”。这些原型必须有自己的函数定义。
显式具体化声明在关键字template后包含<>,而显式实例化没有。
完全匹配允许的无关紧要的转换:
从实参 | 到形参 | Type | Type & | Type & | Type | Type [] | * Type | Type(argument-list) | Type (*)(argument-list) | Type | const Type | Type | volatile Type | Type * | const Type | Type * | volatile Type * | 指向非const数据的指针和引用]优先与非const指针和引用参数匹配。
其中一个是非模板函数,而另一个不是。在这种情况下,非模板函数将优先于模板函致(包括显式具体化)。
如果两个完全匹配的函数都是模板函数,则较具体的模板函数优先。
template<class T1, class T2>
void ft(T1 x, T2 y)
{
?type? xpy = x + y;
}
以上代码中xpy的类型无法明确,可以通过C++11新增关键字decltype来解决:
template<class T1, class T2>
void ft(T1 x, T2 y)
{
decltype(x + y) xpy = x + y;
}
template<class T1, class T2>
?type? ft(T1 x, T2 y)
{
return x + y;
}
以上代码中返回类型无法明确,可以通过auto关键字和关键字decltype组合使用来解决:
template<class T1, class T2>
auto ft(T1 x, T2 y) -> decltype(x + y)
{
return x + y;
} |
|