short
unsigned
const_cast
goto
signed
using
continue
if
sizeof
virtual
default
inline
static
void
delete
int
static_cast
volatile
do
long
struct
wchar_t
double
mutable
switch
while
dynamic_cast
namespace
template
3.3标识符4、数据类型4.1基本数据类型
七种基本的C++数据类型:bool、char、int、float、double、void、wchar_t
类型修饰符:signed、unsigned、short、long
注:一些基本类型可以使用一个或多个类型修饰符进行修饰,比如:signed short int简写为short、signed long int 简写为long。
类型名占用字节数数值范围
void
bool
{true.false}
wchar_t
2或4个字节
char(signed char)
-128~+127
short(signed short)
-32768~+32767
int(signed int)
-2147483648~+2147483647
long(signed long)
-2147483648~+2147483647
long long(signed long long)
-9,223,372,036,854,775,808 ~9,223,372,036,854,775,807
float
-.34*1038~3.4*1038
double
-1.7*10308~1.7*10308
unsigned char
0~255
unsigned shrot
0~65525
unsigned(unsigned int)
0~4294967295
unsigned long
0~4294967295
unsigned long long
0 ~ 18,446,744,073,709,551,615
//x64处理器 64位window10 vs2015 #includeusing namespace std; int main() { bool b; char c;short s; int i; long l; long long ll; float f; double d; long double ld;long float lf; unsigned char uc; unsigned short us; unsigned int ui; unsigned long ul; unsigned long long ull; cout << sizeof(bool) << endl; cout << sizeof(char)<<" " << sizeof(short)<<" "<< sizeof(signed int) << " " << sizeof(long) << " " << sizeof(signed long long) << " " << sizeof(float) << " " << sizeof(double) << " " << sizeof(long float) << " " << sizeof(long double) << endl; cout < 4.2 数据类型在不同系统中所占空间大小
这个与机器、操作系统、编译器有关。比如同样是在32bits的操作系统系,VC++的编译器下int类型为占4个字节;而tuborC下则是2个字节。
原因:
类型16位操作系统32位操作系统64位操作系统
char
char*
short
int
long
long long
注:long类型在不同编译器中的占位不一样: 32位时WhatsApp网页版,VC++和GCC都是4字节; 64位时,VC++是4字节,GCC是8字节。
4.3 typedef声明
//使用typedef为一个已有的类型取一个新的名字,语法如下: typedef type newname //eg: typedef int feet feet distance4.4 枚举类型
C++中的一种派生数据类型WhatsApp网页版,它是由用户定义的若干枚举常量的集合;枚举元素是一个整型,枚举型可以隐式的转换为int型,int型不能隐式的转换为枚举型。
//枚举类型的语法: enum 枚举名{ 标识符[=整型常数], 标识符[=整型常数], ... 标识符[=整型常数] }枚举变量;如果枚举没有初始化, 即省掉"=整型常数"时, 则从第一个标识符开始;
默认情况下,第一个名称的值为 0,第二个名称的值为 1,第三个名称的值为 2,以此类推。但是,您也可以给名称赋予一个特殊的值,只需要添加一个初始值即可。
例如:
enum course {math,chinese,english,physics,chemistry}c; c = english; cout<5、变量
变量其实只不过是程序可操作的存储区的名称。C++ 中每个变量都有指定的类型,类型决定了变量存储的大小和布局,该范围内的值都可以存储在内存中,运算符可应用于变量上。
5.1 变量的声明和定义
int x = y = z = 66;//错误 int x = 3,y = 3,z = 3; int x, y ,z = 3; x = y = z;变量的声明(不分配内存):extern 数据类型 变量名;
变量的定义:数据类型 变量名1,变量名2,...变量名n;
// 变量声明 extern int a, b; int main () { // 变量定义 int a, b; // 初始化 a = 23; b = 25; return 0; }5.2 变量的作用域
局部变量:在函数或一个代码块内部声明的变量,称为局部变量。它们只能被函数内部或者代码块内部的语句使用。
全局变量:在所有函数外部定义的变量(通常是在程序的头部),称为全局变量。全局变量的值在程序的整个生命周期内都是有效的。
int i = 66; int main () { int i = 88; cout << i<6、运算符
sizeof //返回变量的大小,eg:sizeof(a)返回4 a是整型 sizeof(int) Condition?X:Y //三元运算符 Condition为true,值为X,否则值为Y , //逗号表达式,值为最后一个表达式的值 .和-> //用于引用类、结构和公用体的成员 Cast //强制类型转换符 eg:int(2.202)返回2 & //指针运算符 返回变量的地址 * //指针运算符 指向一个变量运算符优先级
类别运算符结合性
后缀
() -> . ++ - -
从左到右
一元
+ - ! ~ ++ - - (type)* & sizeof
从右到左
乘除
* / %
从左到右
加减
+ -
从左到右
移位
从左到右
关系
< >=
从左到右
相等
== !=
从左到右
位与 AND
从左到右
位异或 XOR
从左到右
位或 OR
从左到右
逻辑与 AND
从左到右
逻辑或 OR
||
从左到右
条件
从右到左
赋值
= += -= *= /= %=>>= 函数
如果传递二维数组,形参必须制定第二维的长度。
形式参数是一个指针:void function(int *param)形式参数是一个已定义大小的数组:void function(int param)形式参数是一个未定义大小的数组:void function(int param)二维数组:void function(int a,int size)
函数返回数组
C++ 不支持在函数外返回局部变量的地址,除非定义局部变量为 static 变量。
int * function(); int** function();8.6 获取数组的大小
动态创建(new)的基本数据类型数组无法取得数组大小
int a[3]; //第一种方法 cout<9、函数
函数是实现模块化程序设计思想的重要工具, C++程序中每一项操作基本都是由一个函数来实现的,C++程序中只能有一个主函数(main)
9.1 函数声明与定义
后两条总结一下就是:调用函数前,程序得知道有这个函数,声明就是提前让程序知道有这么的玩意
函数声明:
函数类型 函数名(参数列表); eg: int max(int a,int b);//声明函数时,a,b可以省略 int max(int,int); void show();函数定义:
函数类型 函数名(参数列表) { 函数体; } eg: int max(int a,int b) { int z; z = a>b?a:b; return z; }9.2 函数的参数与返回值
形参和实参必须个数相同、类型一致,顺序一致
函数传递方式:传值,指针,引用
关于指针和引用后面有详细介绍。
//传值-修改函数内的形式参数对实际参数没有影响 int add(int value) { value++; return value; } int main() { int v = 10; cout << "add() = " << add(v) << endl;//add() = 11 cout << "v = " << v << endl;//v = 10 return 0; } //指针-修改形式参数会影响实际参数 int add(int* pValue) { (*pValue)++; return *pValue; } int main() { int v = 10; cout << "add() = " << add(&v) << endl;//add() = 11 cout << "v = " << v << endl;//v = 11 return 0; } //引用-修改形式参数会影响实际参数 int add(int &value) { value++; return value; } int main() { int v = 10; cout << "add() = " << add(v) << endl;//add() = 11 cout << "v = " << v << endl;//v = 11 return 0; }有默认值参数的函数
int sum(int a, int b=2) { return (a + b); } int main () { cout << "Total value is :" << sum(100, 200);<< endl;//Total value is :300 cout << "Total value is :" << sum(100);<< endl;//Total value is :102 return 0; }函数的返回值
9.3 函数调用
函数可以单独作为一个语句使用。有返回值的函数,可将函数调用作为语句的一部分,利用返回值参与运算。
函数调用形式:参数传递–>函数体执行–>返回主调函数
函数名(实参列表); show();函数的嵌套调用:
int a() { return 666; } int b(int sum) { return sum+a() } int main() { cout<函数的递归调用:直接递归调用和间接递归调用
//直接递归调用:求1+...n的值 int total(int sum) { if (sum == 1) { return 1; } return sum + total(sum - 1); } int main() { cout << "total = " << total(10) << endl;//total = 55 system("pause"); return 0; } //间接递归调用 int f2(); int f1() { ... f2() } int f2() { f1(); }9.4 函数重载
同一个函数名对应不同的函数实现,每一类实现对应着一个函数体,名字相同,功能相同,只是参数的类型或参数的个数不同。
多个同名函数只是函数类型(函数返回值类型)不同时,它们不是重载函数
int add(int a,int b) { return a+b; } double add(double a,double b) { return a+b; } int add(int a,int b,int c) { return a+b+c; }9.5 内联(inline)函数
c++在编译时可以讲调用的函数代码嵌入到主调函数中,这种嵌入到主调函数中的函数称为内联函数,又称为内嵌函数或内置函数。
内联函数格式如下:
inline 函数类型 函数名(形参列表) { 函数体; } inline int add(int a, int b) { return a + b; }9.6 洞悉内联函数底层原理
1.使用Visual Studio 2015创建一个C++Win32控制台程序,点击项目->项目属性设置内联函数优化
2.编写内联函数代码,设置断点,debug启动
#include#include using namespace std; inline int add(int a, int b) { return a + b;//断点1 } int main() { int result = add(12, 34); cout << result << endl;//断点2 return 0; } 3.调试->窗口->反汇编,然后就能看到编译后的汇编程序
... int result = add(12, 34); 00B620DE mov eax,0Ch 00B620E3 add eax,22h //对eax中和22h中值进行相加,赋值给eax 00B620E6 mov dword ptr [result],eax cout << result << endl; 00B620E9 mov esi,esp 00B620EB push offset std::endl> (0B610A5h) 00B620F0 mov edi,esp 00B620F2 mov eax,dword ptr [result] 00B620F5 push eax 00B620F6 mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (0B6D098h)] 00B620FC call dword ptr [__imp_std::basic_ostream >::operator<< (0B6D0A8h)] 00B62102 cmp edi,esp 00B62104 call __RTC_CheckEsp (0B611C7h) 00B62109 mov ecx,eax 00B6210B call dword ptr [__imp_std::basic_ostream >::operator<< (0B6D0ACh)] 00B62111 cmp esi,esp 00B62113 call __RTC_CheckEsp (0B611C7h) return 0; 4.从汇编代码中可以代码编译后内联函数直接嵌入到主函数中,并且断点1不会执行到,下面是没使用内联函数(去掉inline关键字)的汇编代码:
int result = add(12, 34); 00291A4E push 22h 00291A50 push 0Ch 00291A52 call add (02914D8h) //调用add函数 00291A57 add esp,8//移动堆栈指针esp,继续执行主函数 00291A5A mov dword ptr [result],eax cout << result << endl; 00291A5D mov esi,esp 00291A5F push offset std::endl> (02910A5h) 00291A64 mov edi,esp 00291A66 mov eax,dword ptr [result] 00291A69 push eax 00291A6A mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (029D098h)] cout << result << endl; 00291A70 call dword ptr [__imp_std::basic_ostream >::operator<< (029D0A8h)] 00291A76 cmp edi,esp 00291A78 call __RTC_CheckEsp (02911C7h) 00291A7D mov ecx,eax 00291A7F call dword ptr [__imp_std::basic_ostream >::operator<< (029D0ACh)] 00291A85 cmp esi,esp 00291A87 call __RTC_CheckEsp (02911C7h) system("pause"); 00291A8C mov esi,esp 00291A8E push offset string "pause" (0299B30h) 00291A93 call dword ptr [__imp__system (029D1DCh)] 00291A99 add esp,4 00291A9C cmp esi,esp 00291A9E call __RTC_CheckEsp (02911C7h) return 0; 从以上代码代码可以看出,在主函数中调用(call)了add函数。
5.在内联函数中添加几个循环后,编译器就把内联函数当做普通函数看待了,代码如下:
inline int add(int a, int b) { int sum = 0; for (int i = 0; i < 100; i++) a++; for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) { sum++; } } return a + b; } int main() { int result = add(12, 34); cout << result << endl; return 0; }int result = add(12, 34); 00181A4E push 22h 00181A50 push 0Ch 00181A52 call add (01814ECh) /// 00181A57 add esp,8 00181A5A mov dword ptr [result],eax cout << result << endl; 00181A5D mov esi,esp 00181A5F push offset std::endl> (01810A5h) 00181A64 mov edi,esp 00181A66 mov eax,dword ptr [result] 00181A69 push eax 00181A6A mov ecx,dword ptr [_imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A (018D098h)] cout << result << endl; 00181A70 call dword ptr [__imp_std::basic_ostream >::operator<< (018D0A8h)] 00181A76 cmp edi,esp 00181A78 call __RTC_CheckEsp (01811C7h) 00181A7D mov ecx,eax 00181A7F call dword ptr [__imp_std::basic_ostream >::operator<< (018D0ACh)] 00181A85 cmp esi,esp 00181A87 call __RTC_CheckEsp (01811C7h) return 0; 00181AA3 xor eax,eax 10、字符串(string)10.1 C风格的字符串(字符数组)
C风格的字符串实际上是使用 null 字符 ‘\0’ 终止的一维字符数组。
输入字符串长度一定小于已定义的字符数组长度,最后一位是/0终止符号;不然输出时无法知道在哪里结束。
字符数组的定义和初始化
char a[5] //字符个数不够,补0; 字符个数超过报错 char str[7] = {'h','e','i','r','e','n'}; char str[] = {'h','e','i','r','e','n'}; cin>>str;//输入 输入字符串长度一定小于已定义的字符数组长度 cout<字符串的处理函数
strcat(char s1[],const char s2[]);//将s2接到s1上 strcpy(char s1[],const char s2[]);//将s2复制到s1上 strcmp(const char s1[],const char s2[]);//比较s1,s2 s1>s2返回1 相等返回1,否则返回-1 strlen(char s[]);//计算字符串s的长度 字符串s的实际长度,不包括\0在内10.2 C++中的字符串(string)
字符串的定义和初始化
//定义 string 变量; string str1; //赋值 string str2 = "ShangHai"; string str3 = str2; str3[3] = '2';//对某个字符赋值 //字符串数组 string 数组名[常量表达式] string arr[3];字符串的处理函数
#include#include #include string str;//生成空字符串 string s(str);//生成字符串为str的复制品 string s(str, strbegin,strlen);//将字符串str中从下标strbegin开始、长度为strlen的部分作为字符串初值 string s(cstr, char_len);//以C_string类型cstr的前char_len个字符串作为字符串s的初值 string s(num ,c);//生成num个c字符的字符串 string s(str, stridx);//将字符串str中从下标stridx开始到字符串结束的位置作为字符串初值 size()和length();//返回string对象的字符个数 max_size();//返回string对象最多包含的字符数,超出会抛出length_error异常 capacity();//重新分配内存之前,string对象能包含的最大字符数 >,>=,<,<=,==,!=//支持string与C-string的比较(如 str<”hello”)。 使用>,>=,<,<=这些操作符的时候是根据“当前字符特性”将字符按字典顺序进行逐一得 比较,string (“aaaa”) 11、指针和引用11.1 指针
指针是一个变量,其值为另一个变量的地址。即内存位置的直接地址。
声明的一般形式:
数据类型是指针变量所指向的变量的数据类型,*表示其后的变量为指针变量
数据类型 *指针变量名; int *ip; //整型的指针 double *dp; //double 型的指针 float *fp; //浮点型的指针 char *ch; //字符型的指针指针变量的初始化:
数据类型 *指针变量名 = &变量名; *指针变量名 = &变量名; int a; int *p = &a; int *p2; p2 = &a;指针变量的引用:
int x = 3; int y; int *p; p = &x; y = *p;//y = a指针运算(地址运算)
int arr[10],len; int *p1 = &arr[2],*p2 = &arr[5]; len = p2-p1;//arr[2] 和arr[5]之间的元素个数 3new和delete运算符
指针变量 = new 数据类型(初值); delete 指针变量; delete[] 指针变量;//释放为多个变量分配的地址 int *ip; ip= new int(1); delete ip; int *ip; ip= new int[10]; for (int i = 0; i < 10;i++) { ip[i] = i; } delete[] ip; int a[3][4] = {0};指针与数组
int arr[10]; int *p1 = arr;// *p1 = &arr[0]; int a[3][5] = { 0 }; int(*ap)[5]; ap = a; ap+1;//表示下一个一维数组指针与字符串
11.2 引用
引用可以看做是数据的一个别名,通过这个别名和原来的名字都能够找到这份数据,类似于window中的快捷方式。
int a; int &b = a;//a和b表示相同的变量,具有相同的地址。引用可以作为函数参数,也可以作为函数返回值。
void swap(int &r1, int &r2) { int temp = r1; r1 = r2; r2 = temp; } int &add1(int &r) { r += 1; return r; } int main() { int a = 12; int b = add1(a); cout << a << " "<将引用作为函数返回值时不能返回局部数据的引用,因为当函数调用完成后局部数据就会被销毁。
函数在栈上运行,函数掉用完,后面的函数调用会覆盖之前函数的局部数据。
int &add1(int &r) { r += 1; int res = r; return res; } void test() { int xx = 123; int yy = 66; } int main() { int a = 12; int &b = add1(a); int &c = add1(a); test();//函数调用,覆盖之前函数的局部数据 cout << a << " "<12、自定义数据类型12.1 结构体
结构体可以包含不同数据类型的结构。
定义结构体的一般形式
struct 结构体类型名 { 成员类型1 成员名1; 成员类型2 成员名2; ... ... 成员类型n 成员名n; };结构体变量名的定义和初始化:
//定义结构体同时声明结构体变量名 struct 结构体类型名 { 成员类型1 成员名1; 成员类型2 成员名2; ... ... 成员类型n 成员名n; }变量名1,变量名2,...变量名n; //先定义结构体 [struct] 结构体类型名 变量名; //直接定义 struct { 成员类型1 成员名1; 成员类型2 成员名2; ... ... 成员类型n 成员名n; }变量名1,变量名2,...变量名n; struct person { int year; int age; string name; }p1 = {2019,24,"heiren"}, p1 = { 2020,24,"heiren" }; struct person { int year; int age; string name; }; struct person p1 = { 2019,24,"heiren" }, p1 = { 2020,24,"heiren" }; struct { int year; int age; string name; }p1 = {2019,24,"heiren"}, p1 = { 2020,24,"heiren" };结构体变量的使用:
struct person { int year; int age; string name; }p[2] ={ {2019,24,"heiren"}, { 2020,24,"heiren" }};//可以不指定数组元素个数 p[1].age;结构体作为函数传递有三种:值传递,引用传递,指针传递
12.2 结构体大小和字节对齐
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特 定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐.
为什么需要字节对齐?各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数据。
三个个概念:
可以通过#pragma pack(n)来设定变量以n字节对齐方式
举个例子
//指定对齐值=8 struct st { // 空结构体大小1 char c1;//1 char c2;//2 int i1;//8 int 起始地址按照字节对齐的原理应该是它长度4的整数倍 char c3;//12 short s4;//12 short 起始地址按照字节对齐的原理应该是它长度2的整数倍 12 + 2 = 12 double d;//24 double 起始地址按照字节对齐的原理应该是它长度8的整数倍 12->16 + 8 = 24 char c4;//32 24 + 4 = 28 结构体的总大小为8的整数倍 28->32 int i2;//32 28+4 = 32 int i3;//40 short s5;//40 }; cout << sizeof(st) << endl;//40 //指定对齐值=4 #pragma pack(4) struct st { //1 空结构体大小1 char c1;//1 char c2;//2 int i1;//8 char c3;//12 short s4;//12 double d;//20 char c4;//24 int i2;//28 int i3;//32 short s5;//36 }s; cout << sizeof(st) << endl;//3612.3 公用体(union)
几个不同的变量共享同一个地址开始的内存空间。
定义
union 共同体类型名 { 成员类型1 成员名1; 成员类型2 成员名2; ... ... 成员类型n 成员名n; };初始化
union data { int i; float f; char c; }x = {123}; union data { float f; int i; char c; }; data x = {12.3}; union { char c; int i; float f; }x = {'y‘};引用
共同体变量名.成员名; union data { int i; float f; char c; }x = {12}; int main() { cout << x.i << " " << x.f << " " << x.c << endl;//12 1.68156e-44 x.c = 'c'; cout << x.i <<" "<< x.f << " " << x.c << endl;//99 1.38729e-43 c return 0; }12.4 枚举(enum)和typedef声明
枚举已经在前面的章节介绍过,这里就不在赘述了。
typedef-为已存在的数据类型定义一个新的类型名称,不能定义变量。
typedef声明格式:typedef 类型名称 类型标识符;
typedef char *CP; typedef int INTEGER;13、面向对象13.1 类
类也是一种数据类型。
类的声明:
class 类名 { public: 公有数据成员; 公有成员函数; private: 私有数据成员; 私有成员函数; protected: 保护数据成员; 保护成员函数; };成员函数的定义:类内,类外,类外内联函数
//类外 返回类型 类名:成员函数名(参数列表) { 函数体; } //内联函数:类外 inline 返回类型 类名:成员函数名(参数列表) { 函数体; }内联函数的代码会直接嵌入到主调函数中,可以节省调用时间,如果成员函数在类内定义,自动为内联函数。
13.2 类成员的访问权限以及类的封装类外派生类类内
public
protected
private
13.3 对象
//1.声明类同时定义对象 class 类名 { 类体; }对象名列表; //2.先声明类,再定义对象 类名 对象名(参数列表);//参数列表为空时,()可以不写 //3. 不出现类名,直接定义对象 class { 类体; }对象名列表; //4.在堆上创建对象 Person p(123, "yar");//在栈上创建对象 Person *pp = new Person(234,"yar");//在堆上创建对象注:不可以在定义类的同时对其数据成员进行初始化,因为类不是一个实体,不合法但是能编译运行
对象成员的引用:对象名.数据成员名 或者 对象名.成员函数名(参数列表)
13.4 构造函数
是一种特殊的成员函数,主要功能是为对象分配存储空间,以及为类成员变量赋初值
构造函数定义
//1.类中定义 2.类中声明,类外定义 [类名::]构造函数名(参数列表) { 函数体 }创建对象
类名 对象名(参数列表);//参数列表为空时,()可以不写
带默认参数的构造函数
class Person { public: Person(int = 0,string = "张三"); void show(); private: int age; string name; }; Person::Person(int a, string s) { cout<带参数初始化表的构造函数
类名::构造函数名(参数列表):参数初始化表 { 函数体; } 参数初始化列表的一般形式: 参数名1(初值1),参数名2(初值2),...,参数名n(初值n) class Person { public: Person(int = 0,string = "张三"); void show(); private: int age; string name; }; Person::Person(int a, string s):age(a),name(s) { cout << a << " " << s << endl; }构造函数重载:构造函数名字相同,参数个数和参数类型不一样。
class Person { public: Person(); Person(int = 0,string = "张三"); Person(double,string); void show(); private: int age; double height; string name; }; ...拷贝构造函数
类名::类名(类名&对象名) { 函数体; } class Person { public: Person(Person &p);//声明拷贝构造函数 Person(int = 0,string = "张三"); void show(); private: int age; string name; }; Person::Person(Person &p)//定义拷贝构造函数 { cout << "拷贝构造函数" << endl; age = 0; name = "ABC"; } Person::Person(int a, string s):age(a),name(s) { cout << a << " " << s << endl; } int main() { Person p(123, "yar"); Person p2(p); p2.show(); return 0; } //输出 123 yar 拷贝构造函数 age=0 name=ABC13.5 析构函数
是一种特殊的成员函数,当对象的生命周期结束时,用来释放分配给对象的内存空间爱你,并做一些清理的工作。
析构函数的定义:
//1.类中定义 2.类中声明,类外定义 [类名::]~析构函数名() { 函数体; }13.6 对象指针
对象指针的声明和使用
类名 *对象指针名; 对象指针 = &对象名; //访问对象成员 对象指针->数据成员名 对象指针->成员函数名(参数列表) Person p(123, "yar"); Person* pp = &p; Person* pp2 = new Person(234,"yar") pp->show();指向对象成员的指针
数据成员类型 *指针变量名 = &对象名.数据成员名; 函数类型 (类名::*指针变量名)(参数列表); 指针变量名=&类名::成员函数名; (对象名.*指针变量名)(参数列表); Person p(123, "yar"); void(Person::*pfun)(); pfun = &Person::show; (p.*pfun)();this指针
每个成员函数都有一个特殊的指针this,它始终指向当前被调用的成员函数操作的对象
class Person { public: Person(int = 0,string = "张三"); void show(); private: int age; string name; }; Person::Person(int a, string s):age(a),name(s) { cout << a << " " << s << endl; } void Person::show() { cout << "age="<age << endl; cout << "name=" < name << endl; } 13.7 静态成员
以关键字static开头的成员为静态成员,多个类共享。
静态数据成员
//类内声明,类外定义 class xxx { static 数据类型 静态数据成员名; } 数据类型 类名::静态数据成员名=初值 //访问 类名::静态数据成员名; 对象名.静态数据成员名; 对象指针名->静态数据成员名;静态成员函数
//类内声明,类外定义 class xxx { static 返回值类型 静态成员函数名(参数列表); } 返回值类型 类名::静态成员函数名(参数列表) { 函数体; } //访问 类名::静态成员函数名(参数列表); 对象名.静态成员函数名(参数列表); 对象指针名->静态成员函数名(参数列表);13.8 友元
借助友元(friend),可以使得其他类中得成员函数以及全局范围内得函数访问当前类得private成员。
友元函数
//1.将非成员函数声明为友元函数 class Person { public: Person(int = 0,string = "张三"); friend void show(Person *pper);//将show声明为友元函数 private: int age; string name; }; Person::Person(int a, string s):age(a),name(s) { cout << a << " " << s << endl; } void show(Person *pper) { cout << "age="<< pper->age << endl; cout << "name=" << pper->name << endl; } int main() {; Person *pp = new Person(234,"yar"); show(pp); system("pause"); return 0; } //2.将其他类的成员函数声明为友元函数 //person中的成员函数可以访问MobilePhone中的私有成员变量 class MobilePhone;//提前声明 //声明Person类 class Person { public: Person(int = 0,string = "张三"); void show(MobilePhone *mp); private: int age; string name; }; //声明MobilePhone类 class MobilePhone { public: MobilePhone(); friend void Person::show(MobilePhone *mp); private: int year; int memory; string name; }; MobilePhone::MobilePhone() { year = 1; memory = 4; name = "iphone 6s"; } Person::Person(int a, string s):age(a),name(s) { cout << a << " " << s << endl; } void Person::show(MobilePhone *mp) { cout << mp->year << "年 " << mp->memory << "G " << mp->name << endl; } int main() { Person *pp = new Person(234,"yar"); MobilePhone *mp = new MobilePhone; pp->show(mp); system("pause"); return 0; }友元类
当一个类为另一个类的友元时,称这个类为友元类。 友元类的所有成员函数都是另一个类中的友元成员。
语法形式:friend 友元类名
class HardDisk { public: HardDisk(); friend class Computer; private: int capacity; int speed; string brand; }; HardDisk::HardDisk():capacity(128),speed(0),brand("三星"){ } class Computer { public: Computer(HardDisk hd); void start(); private: string userName; string name; int ram; string cpu; int osType; HardDisk hardDisk; }; Computer::Computer(HardDisk hd):userName("yar"),name("YAR-PC"),ram(16),cpu("i7-4710"),osType(64) { cout << "正在创建computer..." << endl; this->hardDisk = hd; this->hardDisk.speed = 5400; cout << "硬盘转动...speed = " << this->hardDisk.speed << "转/分钟" << endl; } void Computer::start() { cout << hardDisk.brand << " " << hardDisk.capacity << "G" << hardDisk.speed << "转/分钟" << endl; cout << "笔记本开始运行..." << endl; } int main() { HardDisk hd; Computer cp(hd); cp.start(); system("pause"); return 0; }13.9 类(class)与结构体(struct)的区别
举个例子:
//结构体默认权限为public struct person { void show(); string name; int age; }; int main() { person p; p.name = "heiren"; p.age = 666; p.show(); cout <<"name="<< p.name <<" age="<< p.age << endl; system("pause"); return 0; }将struct改为class,运行报错。
14、继承和派生14.1 继承和派生概述
继承就是再一个已有类的基础上建立一个新类,已有的类称基类或父类,新建立的类称为派生类和子类;派生和继承是一个概念,角度不同而已,继承是儿子继承父亲的产业,派生是父亲把产业传承给儿子。
一个基类可以派生出多个派生类,一个派生类可以继承多个基类
派生类的声明:
//继承方式为可选项,默认为private,还有public,protected class 派生类名:[继承方式]基类名 { 派生类新增加的成员声明; };继承方式:
继承方式/基类成员public成员protected成员private成员
public
public
protected
不可见
protected
protected
protected
不可见
private
private
private
不可见
利用using关键字可以改变基类成员再派生类中的访问权限;using只能修改基类中public和protected成员的访问权限。
class Base { public: void show(); protected: int aa; double dd; }; void Base::show(){ } class Person:public Base { public: using Base::aa;//将基类的protected成员变成public using Base::dd;//将基类的protected成员变成public private: using Base::show;//将基类的public成员变成private string name; }; int main() { Person *p = new Person(); p->aa = 12; p->dd = 12.3; p->show();//出错 delete p; return 0; }派生类的构造函数和析构函数
class Base { public: Base(int, double); ~Base(); private: int aa; double dd; }; Base::Base(int a, double d) :aa(a), dd(d) { cout << "Base Class 构造函数!!!" << endl; } Base::~Base() { cout << "Base Class 析构函数!!!" << endl; } class Person:public Base { public: Person(int,double,string); ~Person(); private: string name; }; Person::Person(int a,double d,string str):Base(a,d),name(str) { cout << "Person Class 构造函数!!!" << endl; } Person::~Person() { cout << "Person Class 析构函数!!!" << endl; } int main() { cout << "创建Person对象..." << endl; Person *p = new Person(1,2,"yar"); cout << "删除Person对象...." << endl; delete p; system("pause"); return 0; }14.2 多继承
一个派生类同时继承多个基类的行为。
多继承容易让代码逻辑复杂、思路混乱,一直备受争议,中小型项目中较少使用,后来的 Java、C#、PHP 等干脆取消了多继承。
多重继承派生类声明的一般形式:
class 派生类名:继承方式1 基类1,继承方式2 基类2 { 派生类主体; };多重继承派生类的构造函数:
派生类名(总参数列表):基类名1(基类参数列表1),基类名2(基类参数列表2), 子对象名1,...(参数列表) { 构造函数体; }`二义性问题:多个基类中有同名成员,出现访问不唯一的问题。
14.3 虚基类
c++引入虚基类使得派生类再继承间接共同基类时只保留一份同名成员。
class A//虚基类 { protected: int a; }; class B: virtual public A { protected: int b; }; class C:virtual public A { protected: int c; }; class D:public B,public C { protected: int d; void show() { b = 123; c = 23; a = 1; } };应用:c++中的iostream , istream , ostream,base_io
15、多态和虚函数15.1 向上转型
数据类型的转换,编译器会将小数部分直接丢掉(不是四舍五入)
int a = 66.9; printf("%d\n", a);//66 float b = 66; printf("%f\n", b);//66.000000上转型后通过基类的对象、指针、引用只能访问从基类继承过去的成员(包括成员变量和成员函数),不能访问派生类新增的成员
15.2 多态
不同的对象可以使用同一个函数名调用不同内容的函数。
15.3 虚函数
实现程序多态性的一个重要手段,使用基类对象指针访问派生类对象的同名函数。
class A { public: virtual void show() { cout << "A show" << endl; } }; class B: public A { public: void show() { cout << "B show" << endl; } }; int main() { B b; b.show();//B show A *pA = &b; pA->show();//B show 如果show方法前没用virtual声明为虚函数,这里会输出A show system("pause"); return 0; }15.4 纯虚函数
在基类中不执行具体的操作,只为派生类提供统一结构的虚函数,将其声明为虚函数。
class A { public: virtual void show() = 0; }; class B: public A { public: void show() { cout << "B show" << endl; } };抽象类:包含纯虚函数的类称为抽象类。由于纯虚函数不能被调用,所以不能利用抽象类创建对象,又称抽象基类。
16、运算符重载
所谓重载,就是赋予新的含义。函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作。运算符重载(Operator Overloading)也是一个道理WhatsApp网页版,同一个运算符可以有不同的功能。
运算符重载是通过函数实现的,它本质上是函数重载。
允许重载的运算符
运算符名称运算符
双目算术运算符
+、-、*、、、%
关系运算符
==、!=、、=
逻辑运算符
||、&&、!
单目运算符
+、-、*(指针)、&(取地址)
自增自减运算符
++、–
位运算符
|、&、-、……、
赋值运算符
=、+=、-=、*=、/=、%=、&=、!=、^=、=
空间分配和释放
new、delete、new、delete
其他运算符
()(函数调用) 、->(成员访问)、->*(成员指针访问)、,(逗号)、
不允许重载的运算符
运算符名称运算符
成员访问运算符
成员指针访问运算符
. *
域运算符
::
长度运算符
sizeof()
条件运算符
16.1 定义
重载运算符遵循的规则:
一般格式:
函数类型 operator运算符(参数列表) { 函数体 } //举个栗子:定义一个向量类,通过运算符重载,可以用+进行运算。 class Vector3 { public: Vector3(); Vector3(double x,double y,double z); public: Vector3 operator+(const Vector3 &A)const; void display()const; private: double m_x; double m_y; double m_z; }; Vector3::Vector3() :m_x(0.0), m_y(0.0), m_z(0.0) {} Vector3::Vector3(double x, double y,double z) : m_x(x), m_y(y), m_z(z) {} //运算符重载 Vector3 Vector3::operator+(const Vector3 &A) const { Vector3 B; B.m_x = this->m_x + A.m_x; B.m_y = this->m_y + A.m_y; B.m_z = this->m_z + A.m_z; return B; } void Vector3::display()const { cout<<"(" << m_x << "," << m_y << "," << m_z << ")" << endl; }16.2 形式
运算符重载的形式有两种:重载函数作为类的成员,重载函数作为类的友元函数
根据运算符操作数的不同:双目运算符作为类成员函数,单目运算符作为类的成员函数,双目运算符作为类的友员函数,单目运算符作为类的友元函数。
class Vector3 { public: Vector3(); Vector3(double x,double y,double z); public: Vector3 operator+(const Vector3 &A)const; Vector3 operator++(); friend Vector3 operator-(const Vector3 &v1, const Vector3 &v2); friend Vector3 operator--(Vector3 &v); void display()const; private: double m_x; double m_y; double m_z; }; Vector3::Vector3() :m_x(0.0), m_y(0.0), m_z(0.0) {} Vector3::Vector3(double x, double y,double z) : m_x(x), m_y(y), m_z(z) {} //运算符重载 Vector3 Vector3::operator+(const Vector3 &A) const { Vector3 B; B.m_x = this->m_x + A.m_x; B.m_y = this->m_y + A.m_y; B.m_z = this->m_z + A.m_z; return B; } Vector3 Vector3::operator++() { this->m_x ++; this->m_y ++; this->m_z ++; return *this; } void Vector3::display()const { cout<<"(" << m_x << "," << m_y << "," << m_z << ")" << endl; } Vector3 operator-(const Vector3 &v1,const Vector3 &v2) { Vector3 B(v1.m_x - v2.m_x, v1.m_y - v2.m_y, v1.m_z - v2.m_z); return B; } Vector3 operator--( Vector3 &v) { v.m_x--; v.m_y--; v.m_z --; return v; } int main() { Vector3 v1(1, 2, 3); Vector3 v2(2, 3, 2); ++v1;//v1.operator++(); 作为类成员函数可以显式调用 v1.display(); --v2; v2.display(); Vector3 v3 = v1 + v2;// v1.operator+(v2);作为类成员函数可以显式调用 v3.display(); Vector3 v4 = v1 - v2; v4.display(); return 0; }16.3 常用运算符的重载
1.自增自减:
//前置运算符 ++a --a operator++() operator--() operator++(Vector3 &v) operator--(Vector3 &v) //后置运算符 a-- a++ operator++(int) operator--(int) operator++(Vector3 &v,int) operator--(Vector3 &v,int)2.赋值运算符:
String& String::operator=(String &s) { if(this!=&s) { delete[] str; int length = strlen(s.str); str = new char[length+1]; strcpy(str,s.str); } return (*this) }3.输入\输出运算符重载
friend ostream &operator<<( ostream &output, const Vector3 &v ) { output << "F : " <>( istream &input, Vector3 &v ) { input >> v.m_x>> v.m_y>>v.m_z; return input; } 16.4 实现类型转换
类型转换函数的一般形式:
operator 类型名() { 转换语句; } class Vector3 { public: Vector3(); Vector3(double x,double y,double z); public: Vector3 operator+(const Vector3 &A)const; Vector3 operator++(); friend Vector3 operator-(const Vector3 &v1, const Vector3 &v2); friend Vector3 operator--(Vector3 &v,int); operator double() { return m_x + m_y + m_z; } void display()const; private: double m_x; double m_y; double m_z; }; int main() { Vector3 v1(1, 2, 3); double d = v1; cout << d << endl;//6 return 0; }17、IO流
流-一连串连续不断的数据集合。
17.1 流类和对象
输入输出流程:键盘输入=》键盘缓冲区=(回车触发)》程序的输入缓冲区=》‘>>’提取数据