WhatsApp网页版登录WhatsApp网页版登录

WhatsApp中文版

c语言命令行方式是什么_C++基本数据类型详解

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 
#include 
using 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 distance

4.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]之间的元素个数 3

new和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;//36

12.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=ABC

13.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 流类和对象

输入输出流程:键盘输入=》键盘缓冲区=(回车触发)》程序的输入缓冲区=》‘>>’提取数据

相关文章