一篇搞懂C++(万字总结)

一、基础部分

1、C++编写步骤

编写一个C++程序总共分为4个步骤:创建项目->创建文件->编写代码->运行程序

#include
using namespace std;
int main()
{
		cout<<"hello world"<

2、注释

1)单行注释:通常放在一行代码的上方,或一条语句的末尾,对该行代码说明。

//描述信息

2)多行注释:通常放在一段代码的上方,对该段代码做整体说明。

/*描述信息*/

3、变量

作用:给一段指定的内存空间起名,方便操作这段内存。

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

#include
using namespace std;
int main()
{
	int a=10;	
  cout<<"a= "<

4、常量

作用:用于记录程序中不可更改的数据。

1)#define 宏常量:通常在文件上方定义,表示一个常量。

#define 常量名 常量值

2)const修饰的变量:通常在变量定义前加关键字const,修饰该变量为常量,不可修改。

const 数据类型 常量名 = 常量值

5、关键字

关键字是C++中预先保留的单词(标识符)在定义变量或常量时,不要使用关键字。

sizeof关键字

作用:利用sizeof关键字可以统计数据类型所占内存大小

语法:sizeof(数据类型/变量)

#include 
using namespace std;
int main() {
	cout << "short类型所占内存空间为:	" << sizeof(short) << endl;
	cout << "int类型所占内存空间为:	" << sizeof(int) << endl;
	cout << "long类型所占内存空间为:	" << sizeof(long) << endl;
	cout << "long long类型所占内存空间为:	" << sizeof(long long) << endl;
	return 0;
}

6、标识符命名规则

1)标识符不能是关键字;
2)标识符只能由字母,数字,下划线组成;
3)第一个字符必须为字母或下划线;
4)标识符中字母区分大小写。

7、数据类型

C++规定在创建一个变量或常量时,必须要指出对应的数据类型,否则无法给变量分配内存。

转义字符

作用:用于表示一些不显示出来的ASCII字符

常用转义字符:     \n(换行)        \\(反斜线)        \t(水平制表)

数据的输入

作用:用于从键盘获取数据

关键字:cin

语法:cin >> 变量

7.1、整型

作用:整型变量表示的是整数类型的数据。

数据类型                            占用空间                    
short(短整型)                2字节    
int(整型)                              4字节
long(长整型)                  Windows:4字节,Linux:4字节(32位)8字节(64位)
long long(长长整型)      8字节

整型大小比较:short < int <= long <= long long

7.2、浮点型

作用:用于表示小数。

浮点类型数据变量分为两种:1)单精度 float 2)双精度 double

float f1 = 3.14f;
double d1 = 3.14;

两者的区别在于表示的有效数字范围不同:

数据类型            占用字节        有效数字范围
float                    4字节            7位有效数字
double                8字节            15-16位有效数字    

默认情况下,输出一个小数,会显示出6位有效数字。

7.3、字符型

作用:字符型变量用于显示单个字符。

语法:char ch = ‘a';

注意:    1)在显示字符串变量时,用单引号将字符括起来,不要用双引号;

                2)单引号内只能有一个字符,不可以是字符串。

C和C++中字符型变量只占用一个字节。

字符变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放到存储单元。

7.4、字符串类型

作用:用于表示一串字符。

C风格字符串:char 变量名[ ] = “字符串”;

C++风格字符串:string 变量名 = “字符串值”;(使用C++风格字符串时候,要包含#include 这个头文件)

7.8、布尔类型

作用:布尔数据类型代表真或假的值。

bool类型只有两个值:true—真(本质是1) flase—假(本质是0)

bool类型占1个字节大小。

8、运算符

作用:用于执行代码的运算。

运算符类型                            作用                                                                    运算符
算术运算符                            用于处理四则运算                        +     -      *      /      %      ++      --
赋值运算符                            用于将表达式的值赋给变量         =     +=      -=      *=      /=      %=
比较运算符                            用于表达式的比较,并返回一个真值或假值             ==
逻辑运算符                            用于根据表达式的值返回真值或假值        !(与)  &&(与)   ||(或)

9、程序流程结构

C/C++支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构。

  • 顺序结构:程序按顺序执行,不发生跳转;

  • 选择结构:依据条件是否满足,有选择的执行相应功能;

  • 循环结构:依据条件是否满足,循环多久执行某段代码。

9.1、选择结构

1)if语句

作用:执行满足条件的语句

单行格式if语句:

if(条件){
	条件满足执行的语句
	};

多行格式if语句:

if(条件){
	条件满足执行的语句
}
else{
	条件不满足执行语句
};

多条件的if语句:

if(条件1){
	条件1满足执行的语句
}
else if(条件2){
	条件2满足执行的语句
}
else{
	都不满足执行的语句
}

2)嵌套if语句

在if语句中,可以嵌套使用if语句,达到更精准的条件判断。

3)三目运算符

作用:通过三目运算符实现简单的判断

语法:表达式1 ?表达式2:表达式3

如果表达式1的值为真,执行表达式2,并返回表达式2的结果;

如果表达式1的值为假,执行表达式3,并返回表达式3的结果。

4)switch语句

作用:执行多条件分支语句。

switch(表达式)
{
	case结果1:
			执行语句;
			break;
	case结果2:
			执行语句;
			break;
	...
	default:
			执行语句;
			break;
}

9.2、循环结构-while

作用:满足循环条件,执行循环语句。

while(循环条件){
	循环语句
};

9.3、do-while循环语句

作用:满足循环条件,执行循环语句。

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

注意:与while的区别在于do-while会先执行一次循环语句,再判断循环条件。

//在屏幕输出0-9
#include 
using namespace std;
int main() {
	int num = 0;
	do
	{
		cout << num << endl;
		num++;
	} while (num<10);
	return 0;
}
//水仙花数,每个位数上的数字的3次幂之和等于它本身
#include
using namespace std;
int main() {
	int num = 100;//要判断的三位数
	int a = 0;  //个位
	int b = 0;  //十位
	int c = 0;  //百位
	do {
		a = num % 10;      //获取该三位数的个位
		b = num / 10 % 10; //获取该三位数的十位
		c = num / 100;     //获取该三位数的百位 
		if (a * a * a + b * b * b + c * c * c == num) {
			cout << num << "是水仙花数" << endl;
		}		
		num++;
	} while (num < 1000);
	system("pause");
	return 0;
}

9.4、for循环

作用:满足循环条件,执行循环语句。

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

注意:for循环体中的表达式,需要用分号进行分隔。

9.5、嵌套循环

作用:在循环中再嵌套一层循环,解决一些实际问题。

//乘法口诀表
#include 
using namespace std;
int main() {
	for (int i = 1; i < 9; i++)
	{
		for (int j = 0; j <= i; j++)
		{
			cout << j << "*" << i << "=" << j * i << "	";
		}
		cout << endl;
	};
	return 0;
}

9.6、跳转语句—break语句

作用:用于跳出选择结构或循环结构

break使用时机:

  • 出现在switch语句中,用于终止case并跳出switch;

  • 循环语句中,跳出当前的循环语句;

  • 嵌套语句中,跳出最近的内层循环。

9.7、跳转语句—continue语句

作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环。

注意:continue并没有使整个循环终止,而break会跳出循环。

10、数组

所谓数组,就是一个集合,里面存放了相同类型的数据元素。

特点:1)数组中每个数据元素都是相同的数据类型;

2)数组是由连续的内存位置组成的。

10.1、一维数组

一维数组定义方式:
数据类型    数组名[数组长度];
数据类型    数组名[数组长度] = {值1,值2,...}
数据类型    数组名[] = {值1,值2,...}

#include 
using namespace std;
int main() {
	int arr[5];
	int arr2[5] = { 10,20,30,40,50 };
	//利用循环输出数组中的元素
	for (int i = 0; i < 5; i++)
	{
		cout << arr2[i] << endl;
	}
	return 0;
}

数组名的命名规范与变量名命名规范一致,不要和变量名重名。

数组中下标是从0开始索引。

一维数组数组名用途:

  • 可以统计整个数组在内存中的长度 => sizeof(arr)

  • 可以获取数组在内存中的首地址

  • cout<<"数组首地址为:"<< (int)arr << endl;
    cout<<"数组中第一个元素地址为:"<< (int)&arr[0] << endl;

10.2、二维数组

二维数组就是在一维数组上,多加一个维度。

4种定义方式:
数据类型    数组名[行数][列数];
数据类型    数组名[行数][列数] = {{数据1,数据2},{数据3,数据4}};
数据类型    数组名[行数][列数] = {数据1,数据2,数据3,数据4};
数据类型    数组名[][列数] = {数据1,数据2,数据3,数据4};

二维数组名组名用途:1)查看二维数组所占内存空间;2)获取二维数组首地址

11、函数

作用:将一段经常使用的代码封装起来,减少重复代码。

11.1、函数的定义

函数定义的步骤:

返回值类型 -> 函数名 -> 参数列表 -> 函数体语句 -> return表达式

语法:
返回值类型	函数名(参数列表)
{
	函数体语句;
	return表达式;
}
int add(int num1,int num2){
	int sum = num1 + num2;
	return sum;
}

返回值类型:一个函数可以返回一个值,在函数定义中。

函数名:给函数起的名称。

参数列表:使用该函数时,传入的数据。

函数体语句:花括号内的代码,函数内需要执行的语句。

return表达式:和返回值类型挂钩,函数执行完后,返回相应的数据。

11.2、函数的调用

功能:使用定义好的函数。

语法:函数名(参数)

11.3、值传递

所谓值传递,就是在函数调用时实参将数值传入给形参。

值传递时,如果形参发生改变,并不会影响实参。

void swap(int num1,int num2){
	int temp = num1;
	num1 = num2;
	num2 = temp;
	cout <<"交换后"<//定义指针
int a = 10;
int *p;
//让指针记录变量a的地址
p = &a;

使用指针:可以通过引用的方式来找到指针指向的内存。

*p; //指针前加*代表解引用,找到指针指向的内存中的数据

指针所占内存的空间:32位占用4个字节空间; 64位占用8个字节空间

12.2、空指针

空指针:指针变量指向内存中编号为0的空间

用途:初始化指针变量

注意:空指针指向的内存是不可以访问的。

//指针变量p指向内存地址编号为0的空间
int *p = NULL;
//访问空指针报错,内存编号0-255为系统占用内存,不允许用户访问。
cout << *p << endl;

12.3、const修饰指针

const修饰指针的三种情况:
    const修饰指针    ——常量指针
    const修饰常量 ——指针常量
    const即修饰指针,又修饰常量

常量指针特点:指针的指向可以修改,但指针指向的值不可以改。

int a = 10;
int b = 10;
const int *p = &a;
*p = 20;//错
p = &b;//对

指针常量特点:指针的指向不可以改,指针指向的值可以改。

int a = 10;
int b = 10;
int *const p = &a;
*p = 20;//对
p = &b;//错

const即修饰指针,又修饰常量:指针的指向和指针指向的值都不可以改。

技巧:看const右侧紧跟的是指针还是常量,是指针就是常量指针,是常量就是指针常量。

13、结构体

结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。

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

通过结构体创建变量的方式有3种:

  • struct 结构体名 变量名

  • struct 结构体名 变量名 = {成员1值,成员2值,...}

  • 定义结构体时顺便创建变量

struct Student{
	string name;
	int age;
	int score;
}
struct Student s1;
//给s1赋值,通过.访问结构体变量中的属性
s1.name = "张三";
s1.age = 18;
s1.score = 100;

struct Student s2 = {"李四",19,80};
struct Student{
	string name;
	int age;
	int score;
}s3;
s3.name = "王五";
s3.age = 20;
s3.score = 60;

总结: 定义结构体时的关键字是struct,不可省略;

结构体变量利用操作符“.”访问成员。

struct和class的区别:在C++中struct和class唯一的区别在于默认的访问权限不同。struct默认权限为公有,class默认私有。

13.1、结构体数组

作用:将自定义的结构体放到数组中方便维护。

语法:struct 结构体名 数组名[元素个数] = {{},{},{},...}

struct Student{
	string name;
	int age;
	int score;
}
//创建结构体数组
struct Student stuArr[3] = {{"刘",22,100},{"陈",22,100},{"池",22,100}};

13.2、结构体指针

作用:通过指针访问结构体中的成员。

利用操作符“ -> ”可以通过结构体指针访问结构体属性。

//1、创建学生结构体
struct Student s = {"liu",22,100};
//2、通过指针指向结构体
struct Student *p = &s;
//3、通过指针访问结构体变量中的数据
cout <<"姓名是:"<< p->name <

13.3、结构体嵌套结构体

作用:结构体中的成员可以是另一个结构体。

//每个老师辅导一个成员,一个老师的结构体中,记录一个学生的结构体
struct Student{
	string name;
	int age;
	int score;
};
struct Teacher{
	int id;
	string name;
	int age;
	struct Student stu;//子结构学生
};
//创建老师
int main(){
	Teacher t;
	t.id = 1000;
	t.name = "wu";
	t.age = 40;
	t.stu.name = "liu";
	t.stu.age = 22;
	t.stu.score = 80;
}

13.4、结构体做函数参数

作用:将结构体作为参数向函数中传递

传递方式: 值传递 地址传递

struct Student{
	string name;
	int age;
	int score;
};
//值传递
void printStudent(student stu){
	stu.age = 22;
	cout<<"子函数中 年龄:"<< stu.age << endl;
}
//地址传递
void printStudent2(struct Student *p){
	cout<<"年龄:"<< p->age <

如果不想修改主函数中的数据,用值传递,反之用地址传递。

二、提高部分

1、内分区模型

C++程序在执行时,将内存大方向划分为4个区域。

代码区:存放函数体的二进制代码,由操作系统进行管理。
全局区:存放全局变量和静态变量以及常量。
栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

1.1、程序运行前

在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域:

1)代码区:存放CPU执行的机器指令。代码区是共享的,只读的。

2)全局区:存放全局变量、静态变量、常量。该区域的数据在程序结束后由操作系统释放。

1.2、程序运行后

1)栈区:由编译器自动分配释放,存放函数的参数值,局部变量等。

2)堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。

2、引用

作用:给变量起别名。

语法:数据类型 &别名 = 原名

int a = 10;
int &b = a;
b = 20;
cout << a << endl; //输出也为20

注意:1)引用必须初始化;2)引用在初始化后,不可以改变。

int a = 10;
int b = 20;
//int &c; //错误,必须初始化
int &c = a;//一旦初始化,就不可以更改
c = b;//这是赋值操作,不是更改引用

引用的本质:引用的本质在C++内部实现是一个指针常量。

2.1、引用做函数参数

作用:函数传参时,可以利用引用让形参修饰实参。

//1、值传递,形参不会修饰实参
void mySwap1(int a,int b){
	int temp = a;
	a = b;
	b = temp;
}
//2、地址传递,形参修饰实参
void mySwap2(int *a,int *b){
	int temp = *a;
	*a = *b;
	*b = temp;
}
//3、引用传递,形参会修饰实参
void mySwap3(int &a,int &b){
	int temp = a;
	a = b;
	b = temp;
}
//总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更加清晰。

3、函数提高

3.1、函数默认参数

语法: 返回值类型 函数名(参数 = 默认值){}

int func(int a, int b = 10, int c =10){
	return a + b + c;
}

注意: 1)如果某个函数参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值;

2)如果函数声明有默认值,函数实现的时候就不能有默认参数。

3.2、函数重载

作用:函数名可以相同,提高复用性。

函数重载满足条件:1)同一个作用域下;2)函数名相同;3)函数参数类型不同或个数不同或顺序不同

4、封装

C++面向对象三大特性:封装、继承、多态。C++认为万事万物都皆为对象,对象上有其属性和行为。

封装的意义:1)将属性和行为作为一个整体,表现生活中的事物
                    2)将属性和行为加以权限控制

封装意义一:在设计类的时候,属性和行为写在一起,表现事物。

语法:class 类名{访问权限:属性/行为};

//设计一个圆类,求圆的周长
const double PI = 3.14;
class Circle{
	public:			//公共权限
		int m_r;  //属性-半径
		deouble calculatez(){
			return 2 * PI * m_r; //行为-获取圆的周长
		}
};
int main(){
	Circle c1; //通过圆类,创建具体的圆(对象)
	c1.m_r = 10; //给圆对象的属性进行赋值
	cout <<"圆的周长:"<< c1.calculatez() << endl;
}

类中的属性和行为,统一称为成员。

属性:成员属性,成员变量。 行为:成员函数,成员方法。

封装意义二:类在设计时,可以把属性和行为放在不同的权限下,加以控制。

三种权限:
公共权限    public            类内可以访问    类外可以访问
保护权限    protected        类内可以访问    类外不可以访问
私有权限    private            类内可以访问    类外不可以访问

成员属性设置为私有优点:

  • 将所有成员属性设置为私有,可以自己控制读写权限;

  • 对于写权限,我们可以检测数据的有效性

5、构造函数和析构函数

5.1、构造函数

主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

语法:类名(){}

  • 构造函数没有返回值也不写void;

  • 函数名称与类名相同;

  • 构造函数可以有参数,因此可以发生重载;

  • 程序在调用对象时会自动调用构造,无须手动调用,而且只会调用一次。

5.2、析构函数

主要作用于对象销毁前系统自动调用,执行一些清理工作。

语法: ~类名(){}

  • 析构函数没有返回值也不写void;

  • 函数名称与类名相同,在名称前面加上符号~;

  • 析构函数不可以有参数,因此不能发生重载;

  • 程序在对象销毁前会自动调用析构函数,无须手动调用,而且只会调用一次。

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

5.3、构造函数的分类及调用

两种分类方式:
1)按参数分为:有参构造和无参构造
2)按类型分为:普通构造和拷贝构造

三种调用方式:1)括号法; 2)显示法; 3)隐式转换法

//拷贝构造函数
Person(const Person &p){
	age = p.age; //将传入的人身上的所有属性拷贝到我身上
}
//调用
Person p1; //默认构造函数调用	注意:调用默认参数时,不要加()
Person p2; //有参构造函数
Person p3; //拷贝构造函数

//显示法
Person p2 = Person(10); //有参构造
Person p3 = Person(p2); //拷贝构造	注意:不要利用拷贝构造函数,初始化匿名对象

//隐式转换法
Person p4 = 10; //相当于Person p4 = Person(10);

5.4、拷贝构造函数调用时机

C++中拷贝构造函数调用时机通常有三种情况:

  • 使用一个已经创建完毕的对象来初始化一个新对象;

  • 值传递的方式给函数参数传值;

  • 以值方式返回局部对象。

5.5、深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作。

浅拷贝带来的问题:堆区的内存重复释放。

浅拷贝的问题要利用深拷贝进行解决:

m_Height = new int(*p.m_Height);

总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。

5.6、初始化列表

作用:C++提供了初始化列表语法,用来初始化属性。

语法:构造函数():属性1(值1),属性2(值2)...{}

class Person{
	public:
		Person(int a,int b,int c){
			m_a = a;
			m_b = b;
			m_c = c;
		}
}
//初始化列表赋初值
Person():m_a(10), m_b(20), m_c(30){}

5.7、静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。

静态成员分为:
1)静态成员变量:所有对象共享同一份数据;在编译阶段分配内存;类内声明,类外初始化。
2)静态成员函数:所有对象共享同一个函数;静态成员函数只能访问静态成员变量。

5.8、const修饰成员函数

1)常函数:成员函数后加const后我们称这个函数为常函数

常函数内一般不可修改成员属性,成员函数声明时加关键字mutable后,在常函数中依然可以修改。

2)常对象:声明对象前加const称该对象为常对象。

常对象只能调用常函数。

6、友元

友元的目的是让一个函数或类访问另一个类中的私有成员。

友元的关键字:friend

友元的三种实现:1)全局函数做友元;2)类做友元;3)成员函数做友元

//1、全局函数做友元
class Building{
	friend void goodGay(Buliding *buliding);
}
//2、类做友元
class Building{
	friend class GoodGay; //GoodGay类是本类的好朋友,可以访问本类中私有成员
}
//3、成员函数做友元
class Building{
	friend void GoodGay::visit();
	...
}

7、继承

继承的好处:减少重复代码。

语法:class 子类:继承方式 父类

子类也称为派生类;父类也称为基类。

派生类的成员,包含两大部分:一类是从基类继承过来的;一类是自己增加的成员。

从基类继承过来的表现其共性,新增成员体现其个性。

7.1、继承方式

继承方式一共有3种:公共继承;保护继承;私有继承

父类所有非静态成员属性都会被子类继承下去。

7.2、继承中构造和析构顺序

子类继承父类后,当创建子类对象,也会调用父类的构造函数。

继承中构造和析构函数顺序:先构造父类,再构造子类,析构顺序与构造顺序相反。

7.3、继承同名成员处理方式

访问子类同名成员:直接访问即可。

s.m_a

访问父类同名成员:需要加作用域。

s.Base::m_a

小结:1)子类对象可以直接访问子类中同名成员;

2)子类对象加作用域可以访问到父类同名成员;

3)当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数。

7.4、多继承

C++允许一个类继承多个类。

语法:class 子类:继承方式 父类1,继承方式 父类2,...

多继承可能会引发父类中有同名成员出现,需要加作用域区分。实际开发不建议使用多继承。

8、多态

多态定义: 同一种行为(调用)导致的不同的结果。

虚函数:虚函数是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。

#include  
using namespace std;
class Shape {
   protected:
      int width, height;
   public:
      Shape( int a=0, int b=0)
      {
         width = a;
         height = b;
      }
      virtual int area()
      {
         cout << "Parent class area :" <area();
   // 存储三角形的地址
   shape = &tri;
   // 调用三角形的求面积函数 area
   shape->area();
   return 0;
}

多态分类:1)静态多态:函数重载和运算符重载都属于静态多态,复用函数名;

2)动态多态:派生类和虚函数实现运行多态。

静态多态和动态多态的区别:

  • 静态多态的函数地址早绑定——编译阶段确定函数地址;

  • 动态多态的函数地址晚绑定——运行阶段确定函数地址。

动态多态满足条件:1)有继承关系;2)子类需要重写父类虚函数

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

Base *base = new son;

重写:函数返回值类型 函数名 参数列表完全一致称为重写。

多态优点:1)代码组织结构清晰;

2)可读性强;

3)利于前期和后期的拓展和维护。

你可能感兴趣的:(c++,蓝桥杯,开发语言,青少年编程)