第1~4章 基础
建议下载下来用Typora软件阅读markdown文件
1~4基础
- 浮点运算的速度通常比整型运算慢,
对于标量运算float和double都不了没有明显差别
对于适量运算double比float慢得多
- 运算符重载(operator overloading):使用相同符号进行多种操作
1.C++内置重载 9/5 int ; 9L/5L long ; 9.0/5.0 double ; 9.0f/5.0f float
2.C++扩展运算符重载
- int guess(3.9832);结果:guess=3; 将浮点float转换为整型int时,采用截取(丢弃小数部分),而不是四舍五入
- 将一个值赋值给取值范围更大的类型通常不会导致什么问题,只是占用的字节更多而已。
- 列表初始化(使用大括号初始化)不允许窄缩(
float-->int
)。 - (long)thorn; long(thron);强制类型转换不会改变thorn变量本身,而是创建一个新的,指定类型的值。
- auto让编译器能够根据初始值的类型推断变量的类型。
- C++的基本类型
- 整数值(内存量及有无符号): bool,char,signed char,unsigned char,short,unsigned short,int,unsigned int,long,unsigned long,(新)long long,unsigned long
- 浮点格式的值:float(32位),double(64位),long double(94~128位)
- 复合类型:数组;字符串:1.字符数组char array 2.string类;结构:struct;共同体:union;枚举:enum;指针:int* ,long*
1.数组(array)
1
2
3
4
5
6short months[12];
int yamcosts[3]={20,30.5};
double earning[4]{1.2e4,1.6e4,1.4e4,1.7e4};
float balances[100]{};//初始化全部元素值为0
//字符串
char boss[8]="Bozo"//后面四个元素为"\0"空字符
using
- using namespace XXX;这是指示
引入名称空间内所有的名称:将XXX名称空间,所有成员变成可见,作用域和using声明一致;例:
using namespace std;
- using XXX;这是声明
引入名称空间或基类作用域内已经被声明的名称:一次只引入一个命名空间成员;
using std::cout;
类之于对象,类型之于变量
对象和变量都是用来描述一段内存的。 - 变量更强调的是变量名这个符号的含义,更强调名字与内存的联系,而不必关注这段内存是什么类型,有多少字节长度,只关注这个变量名a对应着某段内存。
- 而对象的描述更强调的是内存的类型而不在乎名字,也就是说,从对象的角度看内存,就需要清除这段内存的字节长度等信息,而不是关注这个对象在代码中是否有一个变量名来引用这段内存。
2.struct结构
- struct和class的区别
struct能包含成员函数吗? 能!
struct能继承吗? 能!!
struct能实现多态吗? 能!!!
既然这些它都能实现,那它和class还能有什么区别?
最本质的一个区别就是默认的访问控制,体现在两个方面:默认继承访问权限和默认成员访问权限
- 1)默认的继承访问权限。struct是public的,class是private的。
- 2)struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。
- 做个总结,从上面的区别,我们可以看出,struct更适合看成是一个数据结构的实现体,class更适合看成是一个对象的实现体。
3.共用体union
它能够存储不同的数据类型,但只能同时存储其中的一种类型。
这种特性使得当数据项使用两种或更多种格式(但不会同时使用)时,可节省空间。
使用场合:1.对内存的使用苛刻,如嵌入式系统编程 2.操作系统数据结构或硬件数据结构
4.枚举 enum
- 提供了一种创建符号常量的方式,这种方式可以替代const。
- 它还允许定义新的类型,但必须按严格的限制进行。
1
2
3
4
5
6
7
8
9
10
11
12enum spectrum{red,orange,yellow,green,blue,violet,indigo,wltraciolet};//对应整数值0~7(声明定义)
//在不进行强制类型转换的情况下,只能将定义使用的枚举量赋给这种枚举的变量。
spectrum band;//声明定义
band = blue;//初始化(赋值)
//枚举量是整型,可悲提升为int型
int color = blue;
//设置枚举量的值;
enum bits{one=1,two=2,four=4,eight=8};
enum bigstep{first,second=100,third};//first=0,third=101
//枚举的取值范围
bits myflag;
myflag=bits(6);//强制类型转换(整数值),保证bits()输入的参数小茹bits的上限,上限=(2^n-1)>max,max在bits中等于8
一、指针和自由存储空间
1.使用常规变量时,值是指定的量,而地址为派生量。
指针与C++基本原理
1.编译阶段:编译器将程序组合起来
2.运行阶段:程序正在运行时–》oop强调的是在运行阶段进行决策
- 考虑为数组分配内存的情况,C++采用的方法是:使用关键字new请求正确数量的内存以及使用指针来跟踪新分配内存的位置
2.处理存储数据的新策略刚好相反,将地址视为指定的量,将之视为派生量
*运算符
被称为间接值运算符或叫解除引用运算符(对指针解除引用意味着获得指针指向的值)。
&地址运算符
注意:int * p1,p2;
p1是指针,p2是int变量;对于每个指针变量名,都需要一个*
- 定义与初始化
1
2
3
4
5int h = 5;
int *pt =& h;
//或
int *pt;
pt = &h;
应用*
之前,一定要将指针初始化为一个确定的,适当的地址。就是说一定要初始化,否则*pt
将值会赋给一个未知内存。否者都还没引用,又怎么解除引用呢?
- 要将数字值作为地址来使用,应通过强制类型转换将数字转换为适当的地址类型。
1
pt=(int *)0×B8000000;
使用new来分配内存
变量:在编译时分配的有名称的内存。
指针的真正的用武之地在于,在运行阶段分配未命名的内存以及存储值,(C++中使用new运算符来实现)在这种情况下,只能通过指针来访问内存—>所以new的出现都会有指针。
1 | typeName * pointer_name=new typeName;//使用new分配未命名的内存 |
new从被称为堆(heap)或自由存储区(free store)的内存区域分配内存。
delete pointer_name;
//释放指针pointer_name指向的内存。释放pointer_name指向的内存,但不会删除pointer_name指针本身。例如,可以将pointer_name重新指向另外一个新分配的内存块。不要创建两个指向同一内存块的指针对于大型数据对象来说,使用new,如数组、字符串、结构。
- 1.静态联编(static binding)
如果通过声明来创建数组,则程序被编译时将为它分配内存空间,不管程序最终是否使用数组,数组都在那里。它占用了内存,所以必须指定数组长度。
- 2.动态联编(dynamic binding)
意味着数组是在程序运行时创建的,这种数组叫作动态数组。
使用new创建动态数组–>Vector模板类是替代品
1 | //创建 |
指针和数组等价的原因在于指针算术
将整数变量加1后,其值将增加1,
将指针变量加1后,增加的量等于它指向类型的字节数。
- 指针与数组之间的转换
数组:arrayname[i]等价于*(arrayname+i)
指针:pointername[i]等价于*(pointername+i)
因此,很多情况下,可以使用相同的方式使用数组名和指针名
const char *bird ="wren"
bird的值可以修改,但*bird
值不可以修改。其实应该说是不能使用bird
指针来修改!!!
- 常量指针(先出现const 再出现指针 char *):const修饰的是“char * bird”,里面的值是不可以改变的。可以使用指针bird访问字符串“wren”但不能修改字符串。
char * const p ="wren";
创建一个未命名的inflatable类型,并将其地址赋给一个指针。
1 | inflatable *ps=new inflatble |
二、C++有三种管理数据内存的方式(不是说物理结构)
- 自动存储
- 静态存储
- 动态存储–>有时也叫自由存储空间或堆
- 线程存储(C++11新增–>第9章)
自动存储:自动变量(函数内部定义的常规变量)通常存储在栈中
—>随函数被调用生产,随该函数结束而消亡
—>自动变量是个局部变量,作用域为包含的代码块({…})
静态存储:使变量称为静态
- 1.在函数外面定义它
- 2.在声明变量是使用static关键字
static double free = 5650;
动态存储:使用new和delete(可能导致占用的自由存储去不连续)对数据的生命周期不完全受程序或函数的生存周期不完全受程序或函数的生存时间控制。
如果使用new运算符在自由存储(或堆)上创建变量后,没有调用delete。则即使包含指针的内存由于副作用或规则和对象生命周期的原因而被释放(将会无法访问自由存储空间中的结构,因为指向这些内存的指针无效。这将导致内存泄漏),在自由存储空间上动态内存分配的变量或结构也将继续存在。
三、类型组合
数组名是一个指针
- 要用指向成员运算符
1
2
3a_y_e trio[3];
trio[0].year=2003;
(trio+1)->year=2004;
1 | //创建指针数组 |
四、数组的替代品
1.模板类vector–>是一种动态数组–>可以在运行时设置长度–>它是使用new创建动态数组的替代品。
vector类自动通过new和delete来管理内存。
vector<typeName> vt(n_elm);
typeName:类型,vt:对象名,n_elm:个数:整型常量/变量
2.模板类array(C++11)–>与数组一样,array对象长度也是固定的,也使用栈(静态内存分配),而不是自由存储去,因此其效率与数组相同,更方便,更安全。
1
2array<int,5>ai;
array<double,4>ad={1.2,2.1,3.4,4.3};//列表初始化
五、C++的vector、array和数组的比较(都使用连续内存,而list内存空间是不连续的)
在C++11中,STL中提拱了一个新的容器std::array,该容器在某些程度上替代了之前版本的std::vector的使用,更可以替代之前的自建数组的使用。那针对这三种不同的使用方式,先简单的做个比较:
相同点:
三者均可以使用下标运算符对元素进行操作,即vector和array都针对下标运算符[]进行了重载
三者在内存的方面都使用连续内存,即在vector和array的底层存储结构均使用数组
不同点:
vector属于变长容器,即可以根据数据的插入删除重新构建容器容量;但array和数组属于定长容量。
vector和array提供了更好的数据访问机制,即可以使用front和back以及at访问方式,使得访问更加安全。而数组只能通过下标访问,在程序的设计过程中,更容易引发访问 错误。
vector和array提供了更好的遍历机制,即有正向迭代器和反向迭代器两种
vector和array提供了size和判空的获取机制,而数组只能通过遍历或者通过额外的变量记录数组的size
vector和array提供了两个容器对象的内容交换,即swap的机制,而数组对于交换只能通过遍历的方式,逐个元素交换的方式使用
array提供了初始化所有成员的方法fill
vector提供了可以动态插入和删除元素的机制,而array和数组则无法做到,或者说array和数组需要完成该功能则需要自己实现完成。**但是vector的插入删除效率不高(从中间插入和删除会造成内存块的拷贝),但能进行高效的随机存储,list能高效地进行插入和删除,但随机存取非常没有效率遍历成本高。
由于vector的动态内存变化的机制,在插入和删除时,需要考虑迭代的是否失效的问题。
基于上面的比较,在使用的过程中,可以将那些vector或者map当成数组使用的方式解放出来,可以直接使用array;也可以将普通使用数组但对自己使用的过程中的安全存在质疑的代码用array解放出来。