C++中数据分为两种,一种是可以被修改的数据,这种数据一般被成为变量,还有一种数据是常量。
变量:变量在程序中的使用场景有很多,一般常用的数据类型都属于常量。例如 int a;等。
常量:常量是在程序执行期间不会变的。常量是固定值,在程序执行期间不会改变。一般常量的声明一般要使用到关键字(#define和const)。
C++中数据也是有很多类型限制的,这些类型限制我们就叫做数据类型。数据类型分为基础数据类型和复合数据类型。数据也会被其他的关键字进行修饰从而实现数据的不同特性,前面提到的常量就是使用关键字修饰的特性。
当然啊无论是变量还是常量,在使用的过程中他们都有属于自己的作用域,脱离了作用域他么的声明就失去了意义无法使用了。
4.4.1、基础数据类型
基础的数据类型包括一下几种:
数据类型 | 含义 | 大小 |
---|---|---|
bool | bool 类型的值只能是true或者false | 1字节大小 |
char | char类型一般是英文字母或者一些符号。也可以存储一下小数字。 | 1字节大小 |
short | 短整型 | 2字节大小 |
int | int类型是整型数字,常规声明的int为无符号的。至少和short大小一样 | 4字节大小 |
long | 长整型,至少32位,且和int一样 长 | 4字节大小 |
long long | 至少64位,且至少和long一样长 | 8字节大小 |
float | float类型是带有小数数字类型,常规声明也是无符号的 | 4字节大小 |
double | double为高精度的浮点型数据。 | 8字节大小 |
long double | 16字节大小 | |
void | 空类型 | |
nullptr | 空指针类型 | |
wchar_t | 宽字符类型,一般有两个字节表示一个字符,用于大型字符集的使用。 | 2个字节 |
char16_t | 16位字符类型 | 2个字节 |
char32_t | 32位字符类型 | 4个字节 |
基础类型的类型修饰符:
基础类型的类型修饰符主要是修饰的为int、float、double数据类型,将他们修成为有符号、无符号、长整型。短整型等。
signed | 有符号 | |
---|---|---|
unsigned | 默认情况下,定义的变量是有符号 的,加上这个则是无符号修饰 例如:unsigned int a;定义一个无符号的变量a; |
以上修饰类型可以符合修饰基础数据类型。
4.4.2、复合数据类型
符合类型的数据一般都是自定义的数据,复合类型的组成都是基础数据类型组成的。常见的符合类型的数据是类、结构体、联合体、枚举等。
结构体
结构体是用户定义的类型,而结构体的声明定义了这种类型的数据属性。定义类型之后,便可以创建这种类型的变量。
1 结构体声明
结构体的声明需要使用到struct关键字,声明格式如下
struct structName{ //struct 是结构体的声明关键字 structName是定义的类型名
typeName1 xxa; //结构体元素,可以是任意的数据类型,包括基础数据类型和复合数据类型
typeName2 xxb;
}
2 使用方法:
在声明好结构体之后,使用主要是是通过结构体的对象。跟定义普通的变量的方式一样定义。在使用结构体中的元素时,通过 “.”访问。
例如:
struct structName{ //struct 是结构体的声明关键字 structName是定义的类型名
int xxa; //结构体元素,可以是任意的数据类型,包括基础数据类型和复合数据类型
char xxb;
}
//定义结构体对象
structName name1;
name.xxa = 0;//给结构体对象name中的xxa赋值为0
int a = name.xxa; //取出结构体name中的xxa的值赋值给a变量
结构体也支持初始化列表进行初始化
3 使用注意点:
4 与C语言的区别:
1、在C语言中使用结构体在定义实体的时候需要添加struct关键字,而在C++中可以不用添加struct关键字;
例如:
//C语言
struct pople{
char[10] name;
int age;
};
struct pople tom;
//C++
pople jeck;
2、C语言中在结构体中不能添加函数,C++中可以添加函数。
3、与C语言一样,C++也可以使用指定占用位数的结构体成员。
例如:
struct torgle_register{
unsigned int SN:4;//占用4位为SN的值
}
联合体(公用体)
共用体是一种数据格式,它能够存储不同的数据类型,但是只能存储其中的一种数据类型。共用体的声明使用union关键字。
1 作用:
1、进行数据的转换吗,在数据传输中一个int类型的数据要传输四个字节,使用共用体可以减少字节拼接的过程。
2、当数据项使用两种或者更多的数据类型的时候(不同时使用)。可以减少空间的浪费。
2 公用体的声明:
union MyUnion{//union是声明关键字,MyUnion是共用体的类型名;
int a; //共用体的元素
long long_val;
double double_val;
}
3 使用方法:
公用体的使用方式和结构体一样,也是通过 “.” 来访问其中的元素的,唯一的不同就是共用体中在实例使用的时候只能是用其中的一项元素、
4 注意事项:
在使用共用体的时候,需要注意的是共用体开辟的空间是以其中最大元素的值进行开辟。当更换使用的元素类型的时候需要将之前的元素清空,不然当由大元素转换为小元素的时候,由于之前数据没有清除导致出现小元素数值错误的问题。
5 与C语言的区别:
6 匿名共用体:
匿名共用体没有名称,其成员将成为位于相同地址出的变量。
枚举(enum)
枚举提供了一种创建符号常量的方式,这种方式可以代替const。并且他还允许创建新的类型。但是必须严格的限制进行,枚举的定义使用enum关键字。
1 枚举的声明:
enum MyEnum{//enum是定义枚举的关键字,MyEnum 是枚举类型
red, //枚举类型常量一般默然第一个枚举值为0,可以通过=给枚举值赋值
orange,
yellow,
green
};
2 使用方式:
枚举的使用主要有两方面:第一个是与Switch case配合进行分支执行,另一个是状态转换,通过枚举来限制状态的类型。
enum Myenum{
red,
yellow,
green
};//定义一个枚举类型
Myenum enum1;//定义一个枚举对象
enum1 = Myenum::red;//给枚举对象赋值一个枚举变量
if(enum1 == Myenum::red){//判断枚举对象enum1的值是否等于Myenum::red
}
3 注意事项:
枚举类型可以转换为int类型,但是int类型不能转换为枚举类型。
4 与C语言区别:
5 枚举作用域:
在C++11中增加了枚举作用域
指针
指针是一种特殊的数据类型,它是一个可以存储数据变量地址的数据类型。在计算机中,软件中的所有变量都是存储在内存中,一般在内存中一个字节就是一个存储单元。内存单元的编号也叫做地址。那么我们就将这个地址称为指针。严格来说,一个地址就是一个指针,指针是一个常量。但是一般我们在将指针变量也简称为指针。
1 指针中使用的运算符:
在指正的使用中有两个运算符:&和*。&符号是用于获取变量的地址。*符号是用于获取指向地址中的值。
2 指针的声明和初始化:
指针的声明比较简单,在基础变量声明的基础上增加一个*号。例如:
int a;//声明一个int类型的a变量
int *a;//声明一个int类型的指针变量*a;
//需要注意的是,*a是一个指针变量,这是一个整体。
int *a,b;//在这个声明中,声明了一个指针变量*a,变量b;
指针的初始化可以在声明的时候初始化,也可以在后面使用的时候进行初始化。需要注意的就是在使用前一定要初始化,给指针一个确定的,正确的地址。
int a;//声明一个int类型的a变量
int *pa = &a;/声明一个int类型的指针变量*pa,将变量a的地址初始给指针变量
int b;//声明一个int类型的b变量
int *pb;//声明一个int类型的指针变量*pb
pb = &a;//指针变量*pb将变量a的地址赋值给指针变量
3 指针使用的注意事项:
指针使用虽然很方便,但是使用指针有时候会带来毁灭性的问题,以下是几点需要注意的问题:
1、·在C++中创建指针时,计算机会分配用来存储指针变量的地址,而不会分配用来存储指针指向的数据内存,因此在使用指针之前一定先要初始化一个确定的、适当的地址。
2、指针类型显示的地址虽然数整数数字,但是不能将整数赋值给指针,这会导致内存访问修改其他软件或者系统的地址,导致软件或者系统奔溃。
4 指针的使用:
指针的使用无非就是开辟内存、释放内存、给内存中写入值和获取内存中的值。
5 指针内存的开辟:
一般内存的开辟有两种方式,一种是是C语言的malloc开辟空间,一种是使用C++中的关键字new。开辟内存的通用格式:typename * pointer_name = new typename ;
例如:
//方式1
int *a = (int*)malloc(sizeof(int));//使用malloc开辟
int *a = new int;//使用new开辟空间
//方式2
int a;//声明一个int类型的a变量
int *pa = &a;/声明一个int类型的指针变量*pa,将变量a的地址初始给指针变量
以上两种方式都是将int类型的数据赋值给指针对象,但是第一种定义只能通过指针赋值,而第二种可以通过a变量赋值。
6 指针的释放:
当需要使用内存的时候,可以使用new来开辟内存,但是内存在使用结束之后,需要使用delete进行释放,不然会造成内存泄漏。使用delete释放指针的时候只能释放指针指向的内存空间,而不会释放指针对象。指针对象还可以接着使用。
例如:
int *a = (int*)malloc(sizeof(int));//使用malloc开辟
int *a = new int;//使用new开辟空间
free(a);//释放malloc开辟的空间
delete a;//释放new申请的内存空间
在使用delete释放指针的时候需要注意一下两点:
1、指针对象不能使用delete重复释放。
2、delete职能释放new申请的内存空间,而malloc开辟的内存空间需要free进行释放。
3、如果是使用new [ ]申请为数组分配内存,则需要使用delete[ ]来释放。
4、如果使用new []为一个实体分配内存,则应使用delete来释放。
5、对于空指针使用delete是安全的。
7 指针和数组
如果程序只需要一个简单的值,则只需要一个变量。但是对于数组、字符串和结构来说,使用new比较方便。如果通过声明来创建数组,则是在编译阶段就已经开辟好了空间,最后不管程序是否使用数组都是存在的。这种在编译阶段就给数组分配内存被称为静态联编,这意味着在编译时数组就已经添加到程序中了。
如果使用new,这时在运行阶段在需要数组的时候,则创建数组,如果不需要则不创建。这种在程序运行时选择数组长度,这种就被称为动态联编。在运行时创建的数组也叫做动态数组。
8 动态数组的创建
数组分配通用格式:type_name * pointer_name = new type_name [number];
int *a = new int [10];//使用new创建一个int型的数组,数组可放10个数据。
9 动态数组的释放
delete a[];//释放动态数组
10 动态数组使用
动态数组的使用宏观上和普通数组没啥区别,都可以使用[]下标来获取对应位置上的数据。也可以通过指针进行访问,通过指针+1既可以获取下一位的数组值。
例如:
int *a = new int[10];
a[0] = 1;//给动态数组的第一个元素赋值为1
a[1] = 2;//给动态数组的第二个元素赋值为2
对于数组指针,当指针增加1的时候,地址相加的是指针类型的字节数。例如:
int *a = new int[10];
a++;//这里a++,就相当于数组移动到下一个数组元素的地址
总之,使用new来创建的数组以及使用指针来访问不同的元素很简单。
11 指针和字符串
-
char数组名、char指针以及用引用括起来的字符串常量都被解释为第一个字符的地址。
-
在将字符串读入程序时,应使用已分配的内存地址。该地址可以是数组名,也可以使用new来初始化指针。
数组
数组这个概念在C语言中也是有的,在中C++ 支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。
数组声明:typeName arrayName[arraySize];
数组的初始化:数组的初始化只有在定义的时候才能使用初始化,以后就不能使用了,也不能将一个数组的值赋值给另一个数组。数组初始化的时候使用{}括起来,中间的元素使用","隔开。后续使用数据可以通过下标修改和获取数组元素。
但是在C++编程中很少见到数组,那是因为在C++中数组被标准模板的模板类vector代替了。
字符串
字符串是存储在内存中的连续字节中的一系列字符。C++处理字符串的方式有两种,C语言类型的和string类。C语言风格字符串结尾使用。在C++中专门有处理字符串的类,定义字符串除了使用char*之外。最长用的就是string类,使用string关键字就可以简单的定义一个字符串。
例如:
string str;//定义字符串
str = "你好,我是毒蘑菇";//给字符串赋值
引用
引用是C++新增的一种复合类型,引用是已经定义的变量的别名。
1 那么引用有啥作用呢?
引用变量的主要作用就是用作函数形参,通过将引用的变量用作参数,函数将使用原始数据,而不是副本。引用也为函数处理大型结构提供了一种非常方便的途径,同时对于设计类来说引用也是必不可少的。
2 引用的使用
引用的使用很简单,就是在已经定义的变量之后,定义一个引用类型,然后将已经定义的变量初始化给引用。
例如:
int a = 10;//定义一个常见数据类型
int & b = a;//定义一个int型的引用变量b,将a变量初始化给b。
注意:引用只用在定义的时候可以初始化,其他时候不能进行初始化,且初始化的变量是有意义的。
以下就着重介绍一下当引用作为函数参数的使用方法。
3 引用作为函数参数
通常在函数传递的时候,使用最多的就是值传递,而值传递在使用大的数据类型的时候会浪费很大内存空间。
稍微介绍一下值传递的原理:当在函数使用值传递的时候,首先会将实参拷贝给形参。因此在值传递的时候会有一次数据拷贝。由此可见使用值传递将无法修改实参的值,而在计算机编程中会有修改实参值得需求。对此在C语言中可以使用指针传递。而在C++中使用引用将更为安全。
4 引用传递时和值传递的区别
- 函数声明的时候函数参数不同
- 引用传递可以修改实参的值,但是引用传递可以用个const修饰使实参的值不能被修改。被const修饰的引用传递称为常量引用。
- 引用传递不用进行参数拷贝,可以节省时间和内存。
5 引用传递和指针传递的区别
- 函数中声明参数不同。
- 指针传递在函数中需要使用解除引用用算符。
6 临时变量
临时变量是在当实参与引用参数不匹配的时候,C++将生成临时变量。
那么什么时候会使用临时变量?
1、实参的参数类型正确,但不是左值。
2、实参类型不正确,但是可以转换为正确类型。
注意:如果接收引用的参数的函数的意图是修改作为参数的变量的意图,则创建临时变量将组织这种意图的实现。如果声明将引用指定为const,C++将在必要时生成临时变量,实际上,对于形参为const修饰的引用传递如果参数不匹配,则编译器会将类似于按值传递。
临时变量的优点和缺点:
优点:临时变量可以帮助程序员修改参数类型不匹配的问题。
缺点:通过引用将无法修改实参的值。
7 常量引用
常量引用就是在引用的时候使用const关键字修饰,而在使用引用的时候如果没有修改实参的需求的时候尽量使用const修饰引用传递。
8 为什么一定要使用const修饰引用?
1、使用const可以避免无意中修改数据的编程错误。
2、使用const修饰使函数能够处理const和非const实参,不然只能接受非const数据。
3、使用const引用使函数能够正确生成并使用临时变量。
9 引用作为返回值
传统的返回机制是按住传递函数参数类似,使用关键字return返回,在返回的时候这个返回的值会被拷贝到一个临时位置,之后调用程序使用这个值。而当返回引用的时候是直接返回引用的位置,不需要再临时复制。
-
引用返回和值返回区别
引用返回和值返回在声明函数的返回类型书写不同,其他都一样。
-
使用引用返回的注意事项
返回引用应避免返回函数终止时不在存在的内存单元引用。
引用返回最好返回一个作为参数传递给函数的引用。或者返回一个使用new来分配新的存储空间,但是这种分配方式在后面使用delete的时候容易忘记而发生内存泄漏的问题。
-
使用const修饰的引用返回
常规返回类型是右值不能通过地址访问,而通过引用返回的类型是可以被当做左值进行赋值处理。如果返回引用不希望通过赋值改变返回值得数据,那么就可以使用const修饰引用返回类型,这样返回的左值将是不允许修改的。
10 引用和指针的区别
- 引用和指针都是指向内存单元。
- 引用必须在声明的时候初始化,不能先声明再赋值。而指针可以先声明,声明之后再赋值。
11 引用使用的指导原则
- 程序员能修改调用函数中的数据对象。
- 通过传递引用而不是整个数据对象,提高程序运行速度。
- 如果数据对象很小,如内置数据类型或者小型结构,则按值传递。
- 如果数据对象是数组,则使用指针。
- 如果数据对象是较大的结构,则使用const引用。
- 如果数据对象是内置数据类型,则使用指针。
类
类是一个新的数据类型,后续类的介绍,这里简单介绍一下类的定义和使用。当类定义好之后,将类名可以认为是数据类型,只有通过类名可以定义类对象。类对象一般有两种体现形式,一种就是简单定义,这种定义的类是存储在栈中,当超出作用范围之后,就会自动销毁。另一种是指针类对象,这中对象两需要使用new关键进行创建,指针类对象是存储在堆中,在使用之后需要程序员手动释放,释放使用delete关键字。
例如:假设有一个类 myClass
myClass class1;//这就是定义一个普通的类对象。
myClass* class2 = new myClass();//这就是创建类的指针对象。
delete class2;//释放类的指针对象
后续其他的使用在类中介绍。
4.4.3、数据类型的转换
数据类型的转换有两种转换形式,一种是隐式转换,一种是强制转换。这两种转换方式个各有缺点。
隐式转换只能由大转小,例如只能将int类型的转换为short int类型,将float转换为double。
强制转换可以大转小,也可以小转大,但是小转换为大的时候会丢失精度。
其实C++提供了四种类型转换方法,一般情况下使用这四种转换方式,强制转换和隐式转换慢慢的在淘汰。
转换方式 | 含义 | |
---|---|---|
static_cast<需要转换的类型>() | ||
dynamic_cast<需要转换的类型>() | ||
const_cast<需要转换的类型>() | ||
reinterpret_cast<需要转换的类型>() |
4.4.4、变量的作用域
1)快作用域:块作用域指的是在一组大括号中的范围,这个范围中声明的变量作用域就是快作用域。
2)函数作用域:函数作用域说是的就是在函数中定义的数据,这里也包括函数的形参,此作用域中的形参或者在函数中定义的变量都只能在此函数中使用。
3)类作用域:顾名思义类作用域就是在类中定义的数据,一般也成为成员变量,一般都是在类所在的头文件的类中声明。
4)命名空间作用域:C++中引入了命名空间的概念,一般由namespace关键字定义。此作用域中定义的变量只能命名空间下使用。
4.4.5、变量的左值右值
左值(lvalue):指向内存位置的表达式被称为左值(lvalue)表达式。左值可以出现在赋值号的左边或右边。左值的参数是可以被引用的数据对象。常规变量和const变量都可以视为左值,const修饰的变量属于不可被修改的左值。
右值(rvalue):术语右值(rvalue)指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式,也就是说,右值可以出现在赋值号的右边,但不能出现在赋值号的左边
4.4.6、数据存储期
自动存储期
在函数内部定义的常规变量使用的是自动存储期。
静态存储期
静态存储期是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种:一种是在函数外面定义它,另一种是在声明变量是添加static关键字。
动态存储期
new 和delete运算符提供了一种比较自动变量和静态变量更加灵活的方法,他管理了一个内存池,这在C++中被称为自由存储空间或者堆。
const:用来限定变量,将其定义的变量修改为常量。
4.4.7、 变量的别名
C++为类型建立别名的方式有两种。一种是使用预处理器,一种是使用C++和C的关键字typedef来创建别名。
1、使用预处理器
#define BYTE char
2、使用typedef关键字
typedef typeName aliasName;
以上两种方式的使用有些区别,typedef方法不会出现宏替代导致的变量定义不全的问题,typedef能够处理更复杂的类型别名,这使得与使用#define相比,使用typedef是一种更佳的选择。
typedef不会创建新类型,而只是为已有的类型创建一个新的名称。