c++三特性:继承了c语言、面向对象编程、c++模板特性(泛型编程)
面向对象:
OOP 不像过程性编程那样,试图使问题满足语言的过程性方法,而是试图让语言来满足问题的要
求。其理念是设计与问题的本质特性相对应的数据格式——类。
泛型编程:
泛型编程强调独立于特定数据类型,其提供了执行常见任务(如对数据排序或合并链表)的工具,以便可以只编写一个泛型(即不是特定类型的)函数,并将其用于各种实际类型。C++模板提供了完成这种任务的机制。
1.编辑
2.编译(编译器,预处理,编译为汇编代码,汇编代码编译为二进制机器码)
3.链接(链接器对二进制机器码进行链接,将源文件编译后的目标文件和源文件中所使用的函数具体定义文件连接在一起)
4.可执行代码
头文件(include):通常存储在一个名为 "include" 的文件夹中
库文件(lib):库文件包含了编译好的二进制代码,通常是目标文件(.o 或 .obj)的集合,可以被链接到可执行文件中。这些库文件通常存储在一个 "lib" 文件夹中,以便在链接时引用它们。
预处理指令:#include iostream,预处理器处理名称以#开头的编译指令,include编译指令将iostream文件中的内容添加到程序中
(预处理指令:#define作用:1.定义符号常量2.定义符号别名3.定义宏)
对于老式的c头文件保留了扩展名h,c++头文件没有扩展名(旧时c++风格还有),转换后的c头文件没有扩展名
由于C 使用不同的文件扩展名来表示不同文件类型,因用一些特殊的扩展名(如hpp 或hxx表C++头文件是有道理的,ANSI/ISO 委员会也这样认为。问题在于究竟使用哪种扩展名,因此最终他们一致同意不使用任何扩展名。
1.调用函数前使用作用域说明符”::“来指明该函数的命名空间,防止同名函数冲突
2.当该程序中仅有一种命名空间中的函数或者不同命名空间中没有冲突函数,那就可以采用using编译指令,这样便不必每个函数前加上他的名称空间
using namespace std;
3.但是为了防止意外,都加上命名空间
using编译指令的使用
c++程序是一组函数,每个函数又是一组语句。
计算机是一种精确的、有条理的机器。要将信息项存储在计算机中,必须指出信息的存储位置和所需的内存空间。在 C++中,完成这种任务的一种相对简便的方法,是使用声明语句来指出存储类型并提供位置标签。例如 int a
int数据类型指出存储空间,分配和标记由编译器负责
将值赋给存储单元
类是用户定义的一种数据类型,类描述了一种数据类型的全部属性(包括可使用它执行的操作),对象是根据这些描述创建的实体。
函数的最初是函数原型,函数原型存在的目的是为了让编译器在编译过程中认识这个函数,而不用知道他的具体定义,在链接的时候使链接器知道如何找到该函数定义。
函数原型的两种实现方法:
1.在源代码中输入函数原型
2.包含写有函数原型的头文件
不要混淆函数原型和函数定义。可以看出,原型只描述函数接口。也就是说,它描述的是发送给函数的信息和返回的信息。而定义中包含了函数的代码。
常规的声明与赋值语句,对内存单元进行了标识
还有一种方法是指针,指针也可对内存的单元进行标识
由于不同系统中对各数据类型的大小没有统一,所以c++对数据类型的大小也没有特定的规定,要知道系统中整数的最大长度,可以使用sizeof()运算符,单位是字节,但字节的在不同字符集中代表的并不一定是8位。
short 至少16位
int 至少和short一样
long 至少32,且至少和int一样
long long 至少64 且至少和long一样
将赋值语句和声明语句合并在一起
如果不对变量进行初始化,那么他的值将是不确定的,它的值僵尸被创建前相应内存单元保存的值
将变量声明和赋值分开可能带来瞬间悬而未决的问题,所以最好将二者合并一句。
大括号初始化器(也成为列表初始化),可用于任何类型,大括号可以不包含任何东西,此时默认初始化为0,且大括号初始化器更好的防止类型转换错误。
16位无符号范围 0-65536 有符号范围 -32768~-1 0-32767 正负数都是32768个数字
对于赋值超限的情况,数字会对其范围循环加减。比如对-32768减一,其值将变为32767.对65536加一,将变为0.
int类型是计算机中最"自然"的长度,也就是处理效率最高的长度
0-9则位十进制数字
0开的是八进制数
0x开头是十六进制
对以一个没有指定类型的字面值常量比如下:
c++是将1492存储为int、long还是其他类型呢?答案是,除非有理由存储为其他类型(如使用了特殊的后缀来表示特定的类型,或者值太大,不能存储为 int),否则 C++将整型常量存储为 int类型。
char 类型是另一种整型。它足够长,能够表示目标计算机系统中的所有基本符号一一所有的字母、数字、标点符号等。实际上,很多系统支持的字符都不超过 128 个,因此用一个字节就可以表示所有的符号。因此,虽然 char 最常被用来处理字符,但也可以将它用做比 short更小的整型。
对于一些其他字符集,比如国际unicode字符集,需要使用宽字符类型
程序中有时会需要用一些符号表示常量,比如一年多少个月,用Months代表12
方法有使用#define Months = 12 预处理指令,还有一种方法是使用const限定符 const int Months = 12,const限定符指将变量指定为了常量,必须进行初始化,且不允许再次修改。
const比#defien 好。首先,它能够明确指定类型。其次,可以使用 C++的作用域规则将定义限制在特定的函数或文件中(作用域规则描述了名称在各种模块中的可知程度,将在第9章讨论)。第三,可以将 const 用于更复杂的类型,如第4 章将介绍的数组和结构。
c++浮点数表示法有小数点和E、e表示法两种
浮点类型有float、double、long double 三种
浮点常量:程序中未指定类型的浮点数程序会存储为哪种浮点类型呢?默认情况下都是double,加上后缀f、F后缀为float,加上l、L为long double
处理不同类型值时,c++自动进行类型转换
列表初始化(大括号初始化)禁止缩窄转换
让编译器能根据初始化值类型推断变量的类型
数组的声明需要包括 元素类型,数组名,数组元素数
arraysize只能是常量
除了内置复合类型数组,c++标准模板库stl提供了模板类vector,c++11提供了模板类array,他们更加复杂与灵活。
c风格字符串的格式有两种:
第一种使用字符数组,大括号初始化,并对字符使用大量单引号,并且末尾必须加上空字符"\0",以下两个都是char数组,但只有cat是字符串
第二种使用字符数组,使用双引号,不需要显示的加入空字符
enum提供了另一种创建符号常量的方式,可以代替const,#define
地址运算符&
解引用运算符 *
指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。在这种情况下,只能通过指针来访问内存。在C语言中,可以用库函数 malloc()来分配内存:在 C++中仍然可以这样做,但C++还有更好的方法-new 运算符。
示例:int * pn = new int;
这种方法与将变量地址赋给指针:
int a
int * pt = &a
在这两种情况(pn 和 pt下,都是将一个 nt 变量的地址赋给了指针。在第二种情况下,可以通过名称 higgens 来访问该 int,在第一种情况下,则只能通过该指针进行访问。这引出了一个问题:pn 指向的内存没有名称,如何称呼它呢?我们说 pn 指向一个数据对象,它指的是为数据项分配的内存块。
指针创建未命名内存的主要意义在于动态内存分配和管理。这种方式允许你在程序运行时在堆内存中分配一块内存区域,而无需为该内存区域指定一个具体的变量名。
对于指针,需要指出的另一点是,new分配的内存块通常与常规变量声明分配的内存块不同。变量 a 的值存储在被称为栈(stack)的内存区域中,而 new 从被称为堆(heap)或自由存储区(free store)的内存区域分配内存。
虽然指针允许创建未命名内存,并且具有灵活性,但需要小心使用,因为它们可能导致内存泄漏和悬挂指针等问题。因此,在使用指针分配内存时,需要确保在不再需要时正确释放内存。
将指针指向数组,改指针的地址是数组的第一个元素地址
double a[] = {1,2,3};
double * pt = a;
//数组名就是首地址
解引用和定义指针的符号一样 *
C++允许将指针和整数相加。加 1 的结果等于原来的地址值加上指向的对象占用的总字节数。还可以将一个指针减去另一个指针,获得两个指针的差。后一种运算将得到一个整数,仅当两个指针指向同一个数组(也可以指向超出结尾的一个位置)时,这种运算才有意义;这将得到两个元素的间隔。
自动存储是在程序运行过程中自动产生的局部变量,他存储在栈中,当该函数运行时产生,函数将结束时消亡。
静态存储变量有两种方法,一是在函数外面定义他,二是使用关键字static。静态存储与动态存储的关键是严格限制了变量(整型,字符型,浮点型,指针类型等数据类型)的寿命
动态存储是new运算符生成的动态内存,存储在堆中。数据的生命周期不完全受程序或函数控制。
1.使用预处理器 #define
2.typedef
C++从C 语言继承了一个与字符相关的、非常方便的函数软件包,它可以简化诸如确定字符是否为大写字母、数字、标点符号等工作,这些函数的原型是在头文件 cctype(老式的风格中为 ctype.h)中定义的。例如,如果 ch 是一个字母,则 isalpha (ch)函数返回一个非零值,否则返回 0。
integer-expression 整数表达式必须是一个结果为整数值的表达式,它是case指路牌,如果没有匹配的case,则执行default,default标签是可选的。
switch语句在选择一个case后会执行它之后的所有case,而不会停止,除非遇到break,这会使得停止执行switch语句。
argument表示实参,parameter表示形参
c++新增复合类型,引用。引用的主要意义是用作函数的形参,通过将引用变量作为参数,函数可以使用原始数据而不是他的副本。
使用方法,通过函数原型添加默认参数,函数定义中不需要添加默认值。
对于带参数列表的函数,必须从右到左添加默认值,也就是说某个参数有默认值,那他右边所有参数都需要默认值
默认参数能够使用不同数目的参数调用同一个函数,函数重载能使用多个同名当功能不同的函数。
函数重载的关键是函数的参数列表——也成为函数特征标。如果两个函数参数的数目类型以及排列顺序完全相同,则特征标相同。c++允许定义同名函数,条件是他们的特征标不同。
定义:通过传递类型给模板,从而使模板生成该类型的函数。
模板函数定义:
对不同类型使用同一种算法可以使用函数模板,但是有时会使用不同算法,因此产生了函数模板的重载,和函数重载一样,被重载的函数模板使用不同的特征标即可。
当模板重载也不无法解决问题,可提供一个具体化函数定义——显式具体化
1.可以像正常函数调用一样,编译器会根据特征标和返回值类型匹配最合适的模板函数,并将该模板函数实例化为可用的函数,这种实例化方式称为隐式实例化。
2.还可以在函数调用时直接确定函数模板类型,进行显示实例化比如:
a(x,y) //调用a函数模板的int类型
3.还有别的方法等等:
和C语言一样,C++也允许甚至鼓励程序员将组件函数放在独立的文件中。第1章介绍过,可以单独编译这些文件,然后将它们链接成可执行的程序。(通常,C++编译器既编译程序,也管理链接器。)如果只修改了一个文件,则可以只重新编译该文件,然后将它与其他文件的编译版本链接。这使得大程序的管理更便捷。
多个函数就需要每个函数进行一次声明,很麻烦,而且在多个源文件中声明同一个声明,那将会有重复声明的情况出现,因此出现了头文件,头文件可以包括多个声明,而且可以使用if not difine等命令来防止重复声明的现象出现。
在包含头文件时,我们使用“coordin.h”,而不是
作用域(scope)描述了名称在文件(翻译单元)的多大范围内可见。
链接性(linkage)描述了名称如何在不同单元间共享。
自动变量,作用域为局部,没有链接性,也就是作用域是当前函数,只能当前函数使用
静态变量,有三种:外部链接性(可在其他文件中访问)在代码块外部声明;内部链接性(只能在当前文件中访问)在代码块外部声明并用static限定符;无链接性(只能在当前函数或代码块中访问)在函数内部声明并使用static限定符。这3 种接性都在整个程序执行期间存在,与自动变量相比,它们的寿命更长。
动态内存,不由作用域和链接性规则控制。由new创建delete销毁
using编译指令和using 声明之比较
使用 using 编译指令导入一个名称空间中所有的名称:using namespace std
using 声明: using std::cin
过程性编程:对于一个编程问题是一步步考虑并完成编程任务的。
面向对象编程:对于一个编程问题,首先考虑需要使用哪些抽象类,然后对这些类设定数据交互的操作,实现接口与数据存储,最后使用新的设计方案创建出程序。
使用this特殊指针,指向调用成员函数的对象。
类的静态成员类型通常与静态成员变量或静态成员函数一起使用。
静态表示他们是类的特征,而不是具体某个实例,因此可以用于定义与类本身相关的类型和值,而不必创建类的实例。
#include
class MyClass {
public:
// 静态成员类型
typedef int MyType;
// 静态成员变量
static MyType staticVar;
// 静态成员函数,返回静态成员类型
static MyType GetDefaultValue() {
return 42;
}
};
// 初始化静态成员变量
MyClass::MyType MyClass::staticVar = 42;
int main() {
// 使用作用域运算符访问静态成员类型
MyClass::MyType myValue = MyClass::GetDefaultValue();
std::cout << "Default Value: " << myValue << std::endl;
return 0;
}