学习C/C++语言知识整理

我的C/C++语言知识整理

1. 头文件

 #include <iostream>  	//标准输出文件
 #include <stdio.h>		//C语言标准库  输入与输出
 #include <string>		//C++字符串输出(cout)
 #include <fstream>		//读写文件  ofstream 读  ifstream 写
 #include <iomanip>		// 在使用cout 确定输出精度时使用
 #include <ctime>		//time 系统文件包含 使用time()需包含
 /*由于使用rand()时生成的伪随机数不能被当做随机数据来使用,因此需要scran()函数为随机生成器初始化.
 srand((unsigned int)time(NULL));添加随机数种子 作用利用当前系统时间生成随机数,防止每次随机数都一样*/
using namespace std;	//是使用标准命名空间 cout cin 用到

2. 数据类型

2.1常量的定义方式

1.宏常量 #define 大写字母变量名 变量值
利于修改数据,便于后期维护
2.只读变量 (被const 修饰的常量无法修改) const 数据类型 变量名 = 变量初值;
两者相比 最好使用只读变量 宏有缺陷(缺陷未知)
3.全局变量
定义在函数外的变量
4.静态变量
在普通变量前面加一个static,属于静态变量
5.引用
引用基本语法:数据类型 &别名 = 原名;
注意事项:1.引用必须初始化;2.引用在初始化后,不可以改变(若对别名赋值操作,这原名的值也会被赋值)
常量引用,使用场景,用来修饰形参,防止误操作

int temp =10;   
const  int & ref=temp;

引用做函数参数,(引用传递),形参会修饰实参,效果与地址传递相同,优点简化指针修改实参
引用做函数的返回值
1.不要返回局部变量的引用
2.函数调用可以作为左值

    int &  test02()
{
	static int a = 10;//静态变量,存放在全局区,全局区上的数据在程序结束后系统释放
	return a;
}
	int &ref2 = test02();
	cout << "ref2=" << ref2 << endl;//10
	cout << "ref2=" << ref2 << endl;//10
	test02() = 1000;//如果函数的返回值是引用,这个函数的调用可以作为左值
	cout << "ref2=" << ref2 << endl;//1000
	cout << "ref2=" << ref2 << endl;//1000

2.2变量类型

变量创建语法:数据类型 变量名 = 变量初始值;

2.2.1.短整型 short (所占内存 :2B)

2.2.2.整形 int (所占内存 :4B)

2.2.3.长整型 long (所占内存 :4B)

2.2.4.长长整形 long long (所占内存 :8B)

2.2.5.浮点型(单精度) float (所占内存 :4B)

特殊语法:float 变量名 = 变量初值f;(若变量后不加f,一般认为是双精度)
科学计数法
例:float f2 = 3e2; //3*10^2=300
例:float f3 = 3e-2; //3*0.1^2=0.03

2.2.6.双精度 double (所占内存 :8B)

2.2.7.字符型 char (所占内存 :1B)

语法:char 变量名 = ’ ‘;(单引号内为单个字符,例’A’.)
字符型变量对应是ASCⅡ编码:a-97、A-65
char name =‘a’; 等价于 char name = 97;(本质是ascⅡ的转换)
char 可以表示小范围整数(-128~127)

int x = 0;
x = 'a' + 1;//97+1
cout << x << endl;//98

当定义变量初值大于内存范围时

printf("%c\n,225+50);  //305  ->49('I')
//以char(8位)类型输出  305->1 0011 0001 -(char)->00011 0001->49('I')

2.2.8.字符串型 string ,char[ ]

一、C++语言字符串:string
string定义的方式(少见)
(1).string 变量名 (" 变量初值/变量名1");
(2).string 变量名(10,‘a’); <==>string 变量名(“aaaaaaaaaa”);
计算字符串长度
(1).变量名.size()
(2).变量名.length()
判断字符串是否为空(变量名.empty())
empty()判断一个字符,是否为空,如果为空字符串,结果是true(真)
string 类型数据可以使用加法

string s1 = "武当派";
string s2 = "张三丰";
string s3 = "太极拳";
string s4;
s4 = s1 + s2;//"武当派张三丰"
cout << s4 << endl;
s4 += "第一式";//s4=s4+"第一式"

字符串单个字符修改

	string name = "Fred";
	cout << "name=" << name << endl;//Fred

	name[0] = 'L';
	cout << "name=" << name << endl;//Lred

二、C语言字符串
在C语言中,字符串是以“字符数组”存储的

	char name[32] = "Fred";  //char name[32]={'F','r','e','d','\0'};
	/* <==>
	char name [32];
	name[0] = 'F';
	name[1] = 'r';
	name[2] = 'e';
	name[3] = 'd';
	name[4] = '\0';// 翻译字符 0(ASCⅡ)*/
	
	char name[] = "Fred";//char name[5]="Fred";
	printf("szie= %d\n", sizeof(name));//5

字符串比较函数 strcmp(str1,str2) ==0(字符串相同)

2.2.9.布尔型 bool (所占内存 :1B)

cout 输出 true 为 1 ,false 为 0

2.2.10.无符号型 unsigned

语法 :unsigned 数据类型 变量名;
表示0-… 数据存储大小不变,但可以表示更多的正数。

3. 运算符

3.1.赋值运算符

===+=-=*=/=%=
赋值等于加等减等乘等除等余等

3.2.比较运算符

><>=<=!=
大于小于大于等于小于等于不等于

3.3.算数运算符

取余
+-*/%

如果 int类数据 做 除运算 可与用来作取整运算
++1 (- -1)前置递增(减)
1++ (1- -) 后置递增(减)
区别: 前置先进行一次递增(减)再赋值;后置先进行赋值在进行递增(减)

3.4.逻辑运算符

&&//(竖杠)

且:同真为真,其余为假
或:同假为假,其余为真

3.5.转义字符

换行符 \n C中常用 等价 endl
反斜杠 \\ 文件路径中会用到
水平制表符 \t 作用可以整齐的输出数据

3.6.位运算符

位与位或位非异或左移右移
&/(竖杠)~^<<>>

1.位与运算

/*
	0 & 0	0
	0 & 1	0
	1 & 0	0
	1 & 1	1
	对应位 都是1 结果才为1

	8		00001000
	3		00000011
	-----------------
	0		00000000

*/
cout << "8 & 3 = " << (8 & 3) << endl;  //8 & 3 = 0

2.位或运算

/*
	0 | 0	0
	0 | 1	1
	1 | 0	1
	1 | 1	1
	只要有一位为1 ,结果为1

	8		00001000
	3		00000011
	-----------------
	11		00001011  
*/
cout << "8 | 3 = " << (8 | 3) << endl;  //8 | 3 = 11

3.位非运算

//位非 ~
/*
	8		00001000
	~		11110111	
	-----------------
					=>27
*/

unsigned char x = ~8;
cout << (int)x << endl;//27

4.异或

/*	0^0	0
	1^1	0
	0^1	1
	1^0	1

	8:		00001000
	3:^		00000011
	-----------------
			00001011	->11
*/	

cout << (8 ^ 3) << endl;//11

5.左移

//	8		00001000
//	8<<3	01000000

cout << (8 << 3) << endl;	//8*2*2*2=64

6.右移

//无符号数  前面加0
//有符号数  前面原来是多少不变

//	8		00001000
//	8>>1	00000100

cout << (8 >> 1) << endl;	//8/2=0		//-8/2=-4

3.7.运算优先级

( ) [ ] >!>算术运算符>关系运算符>&&>||>赋值运算符>逗号

4. 指针

语法:数据类型 *指针变量名 =& 指向变量名
使用:指针前加一个 * 表示解引用,找到指针指向的内存中的数据
所占空间:4(32位)/8(64位)B

4.1.空指针(初始化指针变量)

(0 ~ 255 之间的内存编号是系统占用的,因此不可以访问。)

4.2.野指针

在程序中,尽量避免使用野指针。
空指针和野指针都不是我们申请的空间,因此不要访问。

4.3.const 指针

const 修饰指针 常量指针

语法:const 数据类型 * 变量名1 = & 变量名2;(例:const int *p = &a;`)
指针指向的值不可以修改,指针指向可以修改

*p=20;  	//错误 		
 p=&b;     //正确

const 修饰常量 指针常量
语法:数据类型 * const 变量名1 = &变量名2;(例:int * const p2 = &a;)
指针的指向不可以改,指针指向的值可以改

*p2=100; 	//正确
p2=&b; 		//错误

const 修饰指针和常量
语法:const 数据类型 * const 变量名1 = & 变量名2;(例:const int * const p3 = &a;
指针的指向 和指针指向的值 都不可以修改

p3 = 100;	//错误
p3 = &b; 	//错误

5. 选择语句

5.1.if 语句

单行if语句
语法:if(判断条件) { 执行体 }

多行if语句
语法:
if (判断条件){ 执行体 }
else{ 执行体 }

多条件if语句
语法:
if(判断条件){ 执行体 }
else if (判断条件){ 执行体 }
…(可多个else if)…
else { 执行体 }

5.2.三目运算符

语法:(变量1 比较运算符 变量2 ? 变量1 : 变量2)
等价于if (变量1 比较运算符 变量2 ){ return 变量1}
else{ return 变量2 }

5.3.switch 语句

//语法:
/*switch(判断变量){
case 变量值:
	执行体
break
//...(与上类似)...
default:
	执行体
break;
}*/

若 case中如果有条件输出相同并列不写输出体

	case 6:
	case 7:
	cout<<x<<endl;   //6、7皆输出x

case里面不能做定义变量

case 0:
int x=0;  ///错误  但可编译
//加{}无错   
case 0{int x =0}

6. 循环语句

6.1.do…while

语法:do { 循环语句 }while(循环条件)

6.2.while

语法:while (循环条件) { 循环语句 }
do…while 循环 和 while 循环的区别:do…while 会先执行一次

6.3.for

语法:for (起始表达式;条件表达式;末尾循环体){循环语句}

7.跳转语句

7.1.break (作用用于跳出 选择结构 或者 循环结构)

break使用时机:
1.出现在switch 条件语句中,作用是终止case并跳出switch
2.出现在循环语句中,作用是跳出当前的循环语句

7.2.continue

作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环
1.continue 直接再次循环不进行continue之后循环体的内容
break 与continue 的区别:break 会退出循环、continue不会退出循环

7.3.goto (可以无条件跳转语句)

如果标记的名称存在,执行到goto语句时,会跳转到标记的位置
语法:
goto 标记;

标记:(可在goto前后)
1.goto 标签; 直接跳到标签处后续代码不进行 只能在一个函数内进行,不能在两个函数之间跳。

8.数组

数组创建语法:数据类型 数组名[ 元素个数 ] (几维数组就有几个[ ])={ 数组元素 }

8.1.数组定义

//例:二位数组
//1.数据类型 数据名 [ 行数 ] [ 列数 ]
int arr[2][3];
//数组内单个元素定义
arr[0][0] = 1;
arr[0][1] = 2;
/*.........*/
arr[1][2] = 6;
//2.数据类型 数组名 [ 行数 ][ 列数 ]={ {数据1,数据2 },{数据3,数据4} ,...};     更加直观,提高代码的可读性
	int arr2[2][3] = {
			{1,2,3},
			{4,5,6}};
//3.数据类型 数组名 [ 行数 ][ 列数 ]={数据1,数据2 ,数据3,数据4,...};
int arr3[2][3] = { 1, 2, 3, 4, 5, 6 };
//4.数据类型 数组名 [][ 列数 ]={数据1,数据2 ,数据3,数据4,...};
int arr4[][3] = { 1, 2, 3, 4, 5, 6 };
//以上定义结果相同

仅定义时(无初始化)不能省略,有初始化可以省略高维

1.数组定义位置不同,初始化不同(不人为添加)
数组的定义如果在全局变量,会初始化为零
如果是局部变量,各元素的值不确定

2.数组初始化
新定义 在定义时,仅确定了部分成员(乱序方式,不是连续方式)
注意:仅C编译器支持该方式,C++编译器不支持

int scores[50] = {
	[15] = 80,
	[20] = 95,
	[25] = 85
};

3.定义函数数组传参
数组作为函数的参数传递,不是单纯的值传递,传递的是数组本身。
当数组作为函数的参数传入到函数中,如果函数对传入的数组内数据进行修改,那么原始数组也会做同样的修改。(传入到的是指针地址)
(1).直接定义参数类型

int a[3][4];
void print1 (int a[3][4]){  }

(2).省略一个高维参数

void print2(int a [][4],int lines)
 //后面的 lines 是可以使程序增加可兼容性,防止内部例如打印输出出现输入数组为[4][4] 输出成 [3][4]

9.函数

函数定义5步:
1.返回值类型 2.函数名 3.参数表列 4.函数体语句 5.return 表达式
语法:
返回值类型 函数名(参数列表)
{
函数体语句

return表达式
}

9.1.函数常见样式

(1).无参无返:void test01()
(2).有参无返:void test02(int a)
(3).无参有返:int test03()
(4).有参有返:int test04(int a)

把数组作为函数的参数时,这个函数中,可以修改这个数组的值(此处调用的是数组的指针)

9.2.默认参数

语法:返回值类型 函数名(形参=默认值){ }
注意事项:
1.如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值
2.如果函数的声明有默认参数,函数实现就不能有默认参数,声明和实现只能有一个默认参数
3.C语言不支持函数的默认参数

9.3.占位参数

语法:返回值类型 函数名 (数据类型){ }
占位参数 还可以有默认参数

9.4.函数重载

可以让函数名相同,提高复用性。
函数重载的满足条件
1.同一个作用域下
2.函数名称相同
3.函数的参数类型不同,或者个数不同,或者顺序不同
注意事项:
1.引用作为重载的条件
2.函数重载碰到默认参数,出现二义性,报错,尽量避免这种情况
3.C不支持函数重载

9.5.函数的缺点:

每调用一次函数,就会为这个函数分配一个“栈”,在计算机底层做很多准备工作(保护原来的执行环境,切换到新的执行环境)有一定的“时间开销”

解决方案:
使用内联函数

内联函数(inline):
当编译器在编译时, 如果遇到内联函数,就会直接将整个函数体的代码插入”调用处”,就相当于内联函数的函数体, 在调用处被重写了一次。以避免函数调用的开销, 获得更快的时间。

inline int add(int a, int b)		//内联函数  在函数前加inline  
{
	return a + b;
}

cout << add(3, 5) << endl;  //使用内联函数则其等价于
cout << 3 + 5 << endl;		//缺点消耗main函数的"栈空间"

内联函数的使用场合:
1)内联函数中的代码应该只是很简单、执行很快的几条语句。
2)这个函数的使用频度非常高,比如在一个循环中被千万次地使用。
数的定义(即整个数体),而不能只出现内联函数的声明。
内联函数的缺点:
使调用内联函数的程序,变得“臃肿”,消耗调用函数的“栈”空间。

9.6.递归函数

定义:在函数的内部,直接或者间接的调用自己。
要点:再定义递归函数时,一定要确定一个“结束条件”!!!
使用场合:处理一些特别复杂的问题,难以直接解决。但是,可以有办法把这个问题变得更简单(转换成一个更简单的问题)。

void test(int n){
	if (n > 3){
			return;
	}
	cout << "进入第" << n << "层梦境...." << endl;
	test(n + 1);
	cout << "退出第" << n << "层梦境...." << endl;
}

	test(1);//进入第1层梦境....进入第2层梦境....进入第3层梦境....退出第3层梦境....退出第2层梦境....退出第1层梦境....

递归函数的缺点:
性能很低!!!
实际开发中, 极少使用!

10.结构体

结构体语法:struct 结构体名{ 结构成员列表 };

10.1.结构体定义:

创建变量结构体的三种方式:
1.struct 结构体名 变量名,通过 “.” 访问结构体变量中的属性
2.struct 结构体名 变量名={成员1值,成员2值…}
3.定义结构体时顺便创建变量
例:struct 结构体名{ 结构成员列表 } s3;

2.结构体数组

结构体数组的语法:struct 结构体名 数组名{元素个数}={ { } , { } , …,{ }}

3.结构体指针

通过指针指向结构体变量

Student *p = &s;

通过指针访问结构体变量中的数据
通过结构体指针 访问结构体值得属性,需要利用“->”

cout << "姓名: " << p->name << " 年龄: " << p->age << " 分数: " << p->score;

11.算法

1.冒泡排序

//冒泡排序函数    参数1  数组的首地址   参数2  数组的长度
void bubbleSort(int *arr,int len)
{
	for (int i = 0; i < len - 1; i++)
	{
		for (int j = 0; j < len - i - 1; j++)
		{
			//如果j>j+1的值  交换数字
			if (arr[j]>arr[j + 1])
			{
				int temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
			}
		}
	}
}

12.输入输出

输出

1.cout (console output)

默认情况,cout 输出6位有效数字
修改输出精度的方法

//1.法1
cout.precision(4);
cout << value << endl;
//法2
//加头文件 <iomanip>
cout << fixed << setprecision(8) << value << endl;
//法3
	double  value = 12.34596789;
	//如果想要这个精度,用来表示小数点后面的位数
	cout.flags(cout.fixed); //定点法:精度,用来表示小数点后面的位数
	cout << value << endl;
	cout << 3.1415926535 << endl;
	cout.unsetf(cout.fixed);//取消订点法
	cout << 3.1415926535 << endl;

2.scanf

出现不安全函数(scanf等)
解决方案 1、修改项目属性,直接使用“不安全”的函数,命令行添加:/D_CRT_SECURE_NO_WARNINGS
解决方案 2、scanf_s

	int x;
	scanf_s("%d",&x);	//不需要使用第3个参数,用法和scanf相同	

	float f;
	scanf_s("%f",&f);	//不需要使用第3个参数,用法和scanf相同	

	char c;
	scanf_s("%c", &c ,sizeof(c));	//需要使用第3个参数,否则警告

	char name[16];					//scanf 字符一旦超过16 破坏内存
	scanf_s("%s", name ,sizeof(name));//需要使用第3个参数

	int age;
	char name[16];
	scanf_s("%d%s",&age,name,sizeof(name));

输入

1.cin

从第一个非空白符(回车符,制表符,空格)开始,直到遇到空白字符为止,和C语言的scanf类似。
1.单个字符顺序读取
(1).输入时会自动跳过空白字符

  • (2).若输入多个字符串,并且输入的字符串个数不确定。当输入数据个数比接受的参数多时,多余的被放到缓冲区

  • (3).当输入的数据类型与接收参数的数据类型不匹配时,如何操作

if (cin.fail()) //如果输入发生错误
{
	cin.clear(); 	//清除cin 的错误标志
	cin.sync();		//清空输入缓冲区
}

(4).cin 位于循环语句的循环条件中
使用cin>>输入时,如果遇到文件结束符(Ctrl + z),就返回零
(5)cin >> 错误
if ((cin >> word) == 0) 出错 类型不相同 返回值为cin.(比如:int word; )
解决方案
1、强制转换if((bool)(cin >> word)) == 0
2、if(!(cin >> word))
2.按行读取 (getline)
语法:getline(cin,存储变量名)
从标准输入设备(cin),读取一行字符串,保存到字符串变量中;读一行,直到遇到回车符,注意不包括回车符;如果用户直接回车,就没有任何输出。
getline 错误
getline(cin, line) >> length; // => cin >> length; //getline 返回值为cin 可以继续输入 第一次输入 rock enter 第二此输入100 enter,则line =rock,length=100.
if (getline(cin,line)==0) //出错 原因与cin相同
解决方案
1、强制转换if((bool)(getline(cin, line)) == 0
2、if(!getline(cin, line))

2.scanf

1.单个词读取

char name[32];
char addr[64];

printf("姑娘芳名?\n");
//输入   小芳\n  ->  输入缓冲区 :小芳\n ->  \n
scanf("%s", name);//==cin>>name;
//清空缓冲区
fflush(stdin); //清空键盘输入设备输入的缓冲区

2.按行读取 gets( )

printf("%s姑娘,家住何地\n", name);
gets(addr);//读一行 ,直到遇到回车符

printf("家住%s 的 %s 姑娘 ,我很中意你\n ", addr, name);

printf("addr的长度:%d\n,", strlen(addr)); //strlen 字符长度

gets 不能使用时,使用gets_s

char line [32];
gets_s(line ,sizeof(line));

13.系统功能

运行暂停…秒数
Sleep(1000); 单位 毫秒

14.读写文件

操作文件的三大类:
1.ofstream : 写文件
2.ifstream : 读文件
3.fstream : 读写文件

文件打开方式
ios::in //为读文件而打开文件
ios::out //为写文件而打开文件
ios::ate //初始位置:文件尾
ios::app //追加方式写文件
ios::trunc //如果文件存在先删除,在创建
ios::binary //二进制方式

操作步骤:
1.包含头文件
2.创建流对象
ofstream 对象名;//写
ifstream 对象名;//读
3.指定打开方式
对象名.open(" ( 文件路径) ",打开方式);
4.写内容
对象名 <<…
5.关闭文件
对象名.close();

读数据的方式

//第一种
	char buf[1024] = { 0 };
	while (ifs>>buf)
	{
		cout << buf << endl;
	}

	//第二种
	char buf[1024] = { 0 };
	while (ifs.getline(buf,sizeof(buf)))//获取一行
	{
		cout << buf << endl;
	}

	//第三种
string buf;
while ( getline (ifs,buf))
	{
		cout << buf << endl;
	}

	//第四种
	char c;
	while ((c=ifs.get())!=EOF)  //EOF  end of file
	{
		cout << c;
	}

15.堆区-new操作

1.new的基本语法
在堆区创建整型数据
堆区的数据由程序员管理开辟,程序员管理释放
如果想释放队堆区的数据,利用关键字delete
内存已经被释放,再次访问就是非法操作,会报错
2、在堆区利用new开辟数组

int * arr =new int[10]

释放数组的时候 要加[ ]才可以

delete[] arr;

16.类和对象

16.1.封装(class)

16.1.1.封装的步骤

//1.class 代表设计一个类,类后面紧跟着的就是类的名称
class 类名  //例:class circle
{
//2.设置访问权限
//公共权限
public:
//3.添加成员属性和行为
//例:int m_r;

//私有权限
private:

}

int main()
{
//4.实例化对象
类名 对象名;	//例:circle c1;
//5.给对象的属性赋值
对象名.成员属性名 = 初值; //例:c1.m_r = 10;

return 0;
}

16.1.2.访问权限

公共权限 public 成员 类内可以访问 类外可以访问
保护权限 protected 成员 类内乐意访问 类外不可以访问 子类也可以访问保护的内容
私有权限 private 成员 类内可以访问 类外不可以访问 子类不可以访问私有内容

成员属性设置为私有
1.可以自己控制读写的权限
2.对于写权限,可以检测数据的有效性

struct和class的区别
struct 默认权限是 公共权限 public
class 默认权限是 私有权限 private

16.2.对象特性

16.2.1.构造函数

构造函数 进行初始化操作
没有返回值 不用写void
函数名 与类名相同
构造函数可以有参数,可以发生重载
创建对象的时候,构造函数会自动地调用,而且只调用一次
(1).构造函数的分类及调用
分类
按照参数分类 无参构造(默认构造) 有参构造
按照类型分类 普通构造 拷贝构造

class Person
{
public:
	//构造函数
	Person()
	{
		cout << "Person 的无参构造函数调用" << endl;
	}

	Person(int a)
	{
		age = a;
		cout << "Person 的有参构造函数调用" << endl;
	}
	//拷贝构造函数
	Person( const Person &p)
	{
		//将传入的人身上的所有属性,拷贝到我的身上
		cout << "Person 的拷贝构造函数调用" << endl;
		age = p.age;
	}
		int age;
};

调用

//1.括号法
Person p1;  //默认构造函数的调用
Person p2(10);//有参构造函数
Person p3(p2);//拷贝函数
//注意事项
	//调用默认构造函数的时候,不要加()
	//因为下面这行代码,编译器会任务是一个函数的声明
	//Person p1();
//2.显示法
	Person p1;
	Person p2=Person(10);  //有参构造
	Person p3 = Person(p2);//拷贝构造
	//Person(10);//匿名对象  特点:当前行执行结束后,系统会立即回收掉匿名的对象
//注意事项2
	//不要利用拷贝构造函数   初始化匿名对象   编译器会认为 Person (p3)===Person p3;
	//Person(p3);
	
//3.隐式转换法
	Person p4 = 10;//相当于  写了  Person p4 = Person(10);
	Person p5 = p4;//拷贝构造

(2).构造函数的调用规则
1.创建一个类,C++编译器会给每个类都添加至少3个函数。
默认构造(空实现)
析构函数(空实现)
拷贝构造(值拷贝)
2.如果我们写了有参构造函数,编译器不在提供默认构造,依然提供拷贝构造
如果我们写了拷贝构造函数,编译器就不再提供其他的普通构造函数
(3).初始化列表

class Person
{
public:
传统初始化操作
//Person(int a, int b, int c)
//{
//	m_A = a;
//	m_B = b;
//	m_C = c;
//}

//初始化列表初始化属性
Person(int a ,int b,int c) :m_A(a), m_B(b), m_C(c)
{

}

int m_A;
int m_B;
int m_C;
};

16.2.2.析构函数 进行清理操作

没有返回值 不写void
函数名和类名相同 在名称前加~
析构函数不可以有参数,不可以发生重载
对象在销毁前 会自动调用析构函数,而且只会调用一次

构造和析构都是必须有的实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构

先构造父类,在构造子类,析构的顺序与构造的顺序相反

16.2.3.类对象作为类成员

当其他类对象作为本类成员,构造时候先构造类对象,在构造自身,析构顺序与构造相反

16.2.4.静态成员函数

所有的对象共享同一个函数
静态成员函数只能访问静态成员变量,不可以访问 非静态成员变量,无法区分到底是哪个对象的
访问静态函数方式:
1.通过对象访问

Person p;
p.func();

2.通过类名来访问

Person::func();

16.2.5.this 指针的用途

this 指针指向 被调用的成员函数 所属的对象
*this 指向的是对象的本体

16.2.6.const 修饰成员函数

this 指针的本质 是指针常量 指针的指向是不可以修改的

const Person * const this;

在成员函数后面加const ,修饰的是this 指向,让指针指向的值也不可以修改

void showPerson() const {  }

16.3.友元

类作友元
friend class 友类名
全局函数作友元
friend 返回值类型 函数名(参数列表);
成员函数作友元
friend 返回值类型 友类名::成员函数( );

16.4.1.继承

继承的好处:减少重复代码
语法:class 子类 : 继承方式 父类
子类 也成为 派生类
父类 也成为 基类
继承方式
公共继承:(public)
父类中的公共权限成员 到子类中已让是公共权限
父类中的保护权限成员 到子类中依然是保护权限
父类中的私有权限成员 子类访问不到
保护继承:(protect)
父类中的公共权限成员 到子类变为保护权限
父类中的保护权限成员 到子类中依然是保护权限
父类中私有成员 子类访问不到
私有继承:(private)
父类中的公共权限成员 到子类变为私有成员
父类中的保护权限成员 到子类中变为私有成员
父类中私有成员 子类访问不到

16.4.2.对象模型

父类中所有非静态成员属性都会被子类继承下去
父类中私有成员的属性被编译器给隐藏了,因此是访问不到,但是确实被继承下去了
查看方式:
利用开人员命令提示工具查看对象模型
跳转盘符 F:
跳转文件路径 cd 具体路径
查看命令
cl /d1 reportSingleClassLayout类名 文件名

16.4.3.同名成员属性处理

如果子类中出现和父类同名成员函数,子类的同名成员函数会隐藏父类中所有的同名成员函数
如果想访问父类最终被隐藏的同名函数 需要加作用域

16.4.4.同名静态成员处理

子类出现和父类同名静态成员函数,也会隐藏父类中所有同名成员函数
如果想访问父类中被隐藏得成员,需要加作用域
//1.通过对象访问

cout << "通过对象访问:" << endl;
Son s;
s.func();
s.Base::func();

2.通过类名访问

cout << "通过类名访问:" << endl;
Son::func();
Son::Base::func();

16.4.5.继承语法

子类 需要继承Base1和Base2
语法:class 子类 :继承方式 父类1 ,继承方式 父类2…

class Son :public Base1, public Base2

16.4.6.菱形继承

利用虚继承 解决菱形继承的问题
在继承之前加上关键字 virtual 变为虚继承

5.多态

动态多态的满足条件
1.有继承关系
2.子类要重写父类的虚函数

动态多态的使用
父类的指针或者引用 指向子类对象

纯虚函数和抽象类
纯虚函数
只要有一个纯虚函数,这个类称为抽象类
抽象类特点:
1.无法实例化对象
2.抽样类的子类 必须要重写父类中的纯虚函数,否则也属于抽象类

虚析构和纯虚析构
纯虚析构 需要 声明 也需要 实现
有了纯虚析构 之后,这类也属于抽象类,无法实例化对象
父类指针在析构时候 不会调用子类中析构函数,导致子类如果有堆区属性,出现内存的泄露
利用虚析构可以解决 父类指针释放子类对象时不干净的问题

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值