引自《进击的巨人》,不要问我为何出自于这,哈哈哈:
什么都无法舍弃的人,什么也改变不了。
1. 函数的重载:
1.1 概念:在同一个作用域中,如果有多个函数的名字相同,但是形参列表不同(参数类型不同或参数个数不同),返回值类型可同也可不同,我们称之为重载函数。重载的函数是通过形参列表区分的,与返回值类型无关。函数重载其实是"一个名字,多种用法"的思想,不仅函数可以重载,运算符也可以重载对函数进行重载事实上可以讲话编程工作和提高代码可读性。
1.2 事实上,重载不是一个真正的面向对象特征,它只是可以简化编程工作的一种方案,而简化工作正是C++语言的全部追求。
1.3 关于函数的重载以下几点需要注意:
- 函数(方法)进行重载一定要谨慎,因为重载函数越多,该程序就越不容易看懂;
- 注意区分重载和覆盖(覆盖后有详述);
- 函数的重载只能进行不同参数的重载,但是不能进行不同返回值的重载;
- 对函数进行重载的目的是为了方便对不同数据类型进行同样的处理。
1.4 main函数不能重载,因为程序的入口只有一个。
1.5 在C++中可以利用函数重载将函数的名字统一起来,函数的名字仅仅是让编译器直到它调用的是哪个函数,用户并不关心函数的名字。而函数重载可以再一定程度上减轻程序员起名字,记名字的负担。
1.6 函数重载编程练习:
要求:当传入一个参数时,计算该参数的平方值;当传入两个参数时,计算两个参数的积;当传入三个参数时,计算三个参数的和。
2. 复杂的数据类型:
2.1 C++的三种复杂数据类型为:数组、指针和结构。
2.2 数组:
- 数组是许多程序设计语言的重要组成部分,尤其在C程序中经常会碰到它们。
- 数组的优点在于,一个数组可以把许多个同类型的值存储在同一个变量名下。
- 数组仍需要被声明为某一种特定的类型:float、char、int等。
- 一般形式:type name[X];
- 注意:绝对不会把不同数据类型的额数据混杂保存在同一个数组中。
- 实例:定义一个数组容纳10个整数,这些整数来自用户输入。将计算这些值的累加和及平均值并输出。
2.3 std::string的工具函数:
- std::length(),取得字符串的长度。
- std::substr(),从字符串中取出一个子串。
- std::at()/operator [],取得字符串中指定位置的字符。
- std::find/rfind(),从前往后/从后往前在字符串中查找一个子串的位置。
- std::find_first_of(),在字符串中找到第一个在指定字符集中的字符位置。
- std::find_first_not_of(),在字符串中找到第一次不在指定字符集中的字符位置。
- std::find_last_of(),在字符串中找到最后一个在指定字符集中的字符位置。
- std::find_last_not_of(),在字符串中找到最后一个不在字符集中的字符位置。
- 将字符串转换为小写/大写:
std::transform(str.begin(), str.end(), str.begin(), tolower);
std::transform(str.begin(), str.end(), str.begin(), toupper);
- 去掉字符串两端的空格:
※ 去掉左边的空格:
str.erase(0, str.find_first_not_of(" /t/n/r"));
※ 去掉右边的空格:
str.erase(str.find_last_not_of(" /t/n/r") + 1);
※ 去掉两边的空格:
str.erase(0, str.find_first_not_of(" /t/n/r")).erase(str.find_last_not_of(" /t/n/r") + 1);
- 忽略大小写比较字符串:先将用于比较的两个字符串各自拷贝一个复本,并将这两个复本转换为小写,然后比较转换为小写之后的两个字符串。
3. 复杂的数据类型——指针:
3.1 程序在硬盘上以文件的形式存在,但它们的运行却是在计算机的内存里发生的。
3.1.1 内存:
- 由一个一个地址组成的,每一个地址内都是一个字节。
- 如下图所示,分别为整型,字符型和浮点型在内存中的存放形式:假设本例中的内存是从0地址开始的,因为C/C++中规定整型,字符型和浮点型分别占用4、1和4个字节,所以具体内存存放形式如图所示。
3.1.2 对齐:
- 对于如上例子,浮点型变量C的值从8开始存储而不是5的原因是:在C++里,变量类型是根据它们的自然边界进行对齐的,一般编译器的处理方式是向CPU看齐。(例如:如果所编译的程序是32位的,那么对齐值就是4个字节。)
- 此外,对齐问题会因为系统平台的不同而不同。实现64位运行速度的条件有三个:1)运行的应用程序是64位的;2)操作系统是64位的;3)CPU是64位的。
3.1.3 寻址:对于变量可以通过两种方法进行索引。
- 变量名
- 地址
“取址”操作符:“&”,它的作用就是获得变量的地址。
3.1.4 补充知识:
1)对齐在计算机底层世界广泛存在,如内存对齐、文件对齐等。
- 程序在编译链接后会被分割成一个一个的区块,而区块在文件和内存中要按照一定的规律来对齐。
※ 一般32位系统内存对齐值是:1000H==4KB;
※ 一般64位系统内存对齐值是:2000H==8KB;
※ 一般文件对齐值是200H,因为存放不需要处理,所以不需要太大的对齐值,因此可以存放的更紧密,所以文件的对齐值可以更小。
- 在内存中如果以单一对齐字节数提交,效率太低,因此内存中的提交单位为“页”,如32位系统中1页含有1000个4字节。而内存和CPU之间提交是4字节为单位进行的。
- 对齐存在的意义是:在程序中数据在高层与低层之间的提交是1页1页进行的。
2)变量的地址在程序执行期间是不会发生变化的,这是各个系统一个普遍的要求。但是,同一个程序不同时间加载到内存中,同一个变量的地址是会改变的。这涉及到windows加载器的原理以及系统对程序的“欺骗”。
3)在windos系统下有两种可执行文件(PE文件),一种是.exe文件,另一种是.dll文件。
3.2 使用指针:
- 地址是计算机内存中的额某个位置,而指针是专门用来存放地址的特殊类型变量。
- 一般用以下形式来声明指针变量:type *pointerName;
- 建议:每一行只声明一个指针变量。
- 在创建指针时,空格的位置不会影响指针的正常创建。
- 这阵变量前边的类型是用来说明指针指向的数据类型,请务必匹配来使用。
- void类型的指针变量void *p; 是合法的,这种情况在声明指针变量时没有声明指针所指内容的类型,当指针被使用时才决定其具体类型。
3.3 利用指针改变值:
- 创建变量时,系统将分配一些内存块用来保存它们的值。
- 每个内存块拥有一个独一无二的地址。
- 变量的地址可以用&variablename语法来取得(注:&被称为“取地址”操作符)。
- 可以把地址赋值给被称为指针的特殊变量。
- 当知道某变量在内存中的地址(通过指针),就可以利用指针访问位于改地址的数据。这需要对指正进行“解引用(Deference)”处理:即在指针名的前面加上一个星号(*)。
3.4 注意:
3.4.1 指针所保存的方式内存中的一个地址。它并不保存指向的数据值本身。因此,务必确保指针对应一个已经存在的变量或者一块已经分配了的内存。
3.4.2 星号有两种用途:其一是用于创建指针(int *myPoint = &myInt;);其二是对指针进行解引用(*myPointer = 3998;)。
3.4.3 C++允许多个指针有相同的值:int *p1 = &myInt; int *p2 = &myInt;。
3.4.4 C++支持无类型(void)指针。但是,对一个无类型指针进行解引用前,必须先把他转换为一种适当的数据类型。
4. 指针和数组:
4.1 计算机把数组以一组连续的内存块保存着,这说明数组拥有很多个地址,每个地址对应一个元素。其实在C、C++中,数组的名字也是一个指针(指向数组的基地址,就是第一个元素的地址)。
4.2 如果想通过指针来访问数组的其他元素,可以采用pointer++的方式,自身运算时不是简单的+1,而是按照指向的数组数据类型来递增的,也就是+sizeof(int)。
4.3 类型转换符reinterpret_cast:
- 作用:允许将任何指针转换为任何其他指针类型。 也允许将任何整数类型转换为任何指针类型以及反向转换。
- 一般形式:reinterpret_cast < type-id > ( expression )
- 滥用 reinterpret_cast 运算符可能很容易带来风险。 除非所需转换本身是低级别的,否则应使用其他强制转换运算符之一。
- reinterpret_cast 运算符可用于 char* 到 int* 或 One_class* 到 Unrelated_class* 之类的转换,这本身并不安全
- reinterpret_cast 运算符不能丢掉 const、volatile 或 __unaligned 特性。
- einterpret_cast 运算符将 null 指针值转换为目标类型的 null 指针值。