C++成长之旅

前提:万能头文件

为什么我们需要万能头文件?

万能头文件可以帮助我们不用像C语言一样挨个引入库

C++万能头文件的引入

Window: https://blog.csdn.net/weixin_52985599/article/details/112301569

MacOS:

C++基础

打印HelloWorld

Hello,World

C++代码

#include <bits/stdc++.h>
using namespace std;
int main(){

cout << "Hello,World" << endl;

return 0;

}

基本数据类型

数据类型 代码
整形 Int
长整形 Long
长长整形 long long
短整形 Short
字符型 Char
单精度浮点型 Float
双精度浮点型 Double

变量名

  • 变量名必须以字母或下划线开头。
  • 变量名可以包含字母、数字和下划线。
  • 变量名不能包含空格或其他特殊字符。
  • 变量名应该具有描述性,以便于代码的可读性和可维护性。

定义变量

形式:数据类型 变量名 = 字面值

示范

int a = 10;
long a = 10;
long long a = 20;
char c = 'a';
float f = 3.23;
double d = 3.24435;

SizeOf

sizeOf是用来获取该数据类型在内存中占用的空间大小

通过以下代码可以看到各个数据类型所占用的内存空间

#include <bits/stdc++.h>
using namespace std;

int main(){
//sizeof用来求出数据类型占用的内存空间

short num = 10;
cout << "short = " << sizeof(num) << endl;

int num1 = 10;
cout << "int = " << sizeof(num1) << endl;

long num2 = 10;
cout << "long = " << sizeof(num2) << endl;

long long num3 = 10;
cout << "long long = " << sizeof(num3) << endl;

float f = 10.23;
cout << "float = " << sizeof(f) << endl;

double d = 10.234;
cout << "double = " << sizeof(d) << endl;

}

浮点型

如果float定义的变量字面值后面不加上f的话会被转化成double类型

float f = 3.14f //这样定义的数据类型才是float类型

科学技术法

float t = 3e2; // 300
cout << "t = " << t;

float t1 = 3e-2; // 0.03
cout << "t1 = " << t1;

字符型

字符型只占用一个内存空间

如何定义字符型

char ch = 'a';

字符和ASCII值的转换

int a = (int)ch;
cout << a << endl;

转义字符

\ + 某一个特殊含义的字符,就可以把他变成子面含义上的字符

示范

\\  -> \
\* -> *
\n ->换行
\t -> 水平制表符 == 四个空格

字符串

C风格的字符串

char[] ch = "helloworld";

C++风格的字符串

string a = "helloworld";

布尔型

布尔型占用一个内存空间

如何定义bool类型

bool flag = true;

整形1 代表 true

整形0 代表false

获取键盘输入

cin(c in) 用来获取键盘输入

示范

string h = "helloh";
cout << "请输入h的值";
cin >> h;
cout << "h = " << h << endl;

If条件判断语句

  • 单if
int a = 10;
int b = 30;
if(a > b){
cout << "a = " << a << endl;
}else {
cout << "b = " << b << endl;
}

解析代码

条件判断如果a大的打印a ,小于等于的情况都打印b

  • 多if
int a = 10;
int b = 30;
if(a > b){
cout << "a = " << a << endl;
}else if(a < b){
cout << "b = " << b << endl;
}else {
cout << "a = b" << endl;
}

通过else…if来判断多种条件

代码解析

如果a > b 打印a,如果a < b打印b,相等的情况打印a = b

如果判断两个值相等,条件判断相等使用==

if(a == b){

}

判断两个值不等 !=

if(a != b){

}

三目操作符

格式:条件判断语句 ?成立执行的结果 :不成立执行的结果

int a = 10;
int b = 20;
int c = 0;
c = a > b ? a : b;
cout << "c = " << c << endl;
return 0;

解析:当a > b 返回结果为a 否则返回结果为b 最终将正确返回的结果传递给c

Switch条件判断语句

以下代码是一个简单的打分系统

cout << "请你为电影打分" << endl;
int score = 0;
cin >> score;
string res = "";
switch(score){
case 10:
res = "我是10";
break;
case 5:
res = "我是5";
break;
case 0:
res = "我是0";
break;
default:
res = "我什么也不是";
break;

}
cout << res << endl;

代码解析

输入一个值放入到score中如果score = 10执行case 10对应的语句,如果为5执行5对应的语句,default是如果这些条件都不满足,执行,default放在最后

while循环

指针

如何定义指针?

int a = 20;
int* p = &a;
cout << "p = " << p << endl; //结果是a的地址
cout << "*p = " << *p << endl; //结果是a的值

*p又叫做解引用

指针占用的内存空间

  • 64位操作系统都占用8
  • 32位操作系统占用4

空指针

如何定义?

int * p = NULL;

注意事项

  • 空指针是指向内存地址中编号为0的空间

  • 空指针用户是无法进行访问的

  • *p = 10; 报错

野指针

野指针指向的是一块非法的内存空间

int* p = (int *)0x1100;

指针常量

指针常量:指针在前,常量在后int* const

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

指针常量值可以进行修改,但是地址无法进行修改

常量指针

常量指针:指针在后,常量在前

const int* p = &a;
//*p = 20;
p = &b;

常量指针值不可以进行修改,地址可以进行修改

Const对地址和值都进行修饰

const int* const p = &a;

这种情况下值和地址都无法进行修改

指针与数组

第一次使用指针的时候访问的数组中的第一个元素

那么指针如何后移呢?

p++就可以实现向后偏移四个字节即一个数组中下一个元素的效果

int arr[5] = {1,2,3,4,5};

cout << arr[0] << endl;

int* p = arr;
//指针指向的是数组的首地址
cout << *p << endl;

//如果想要访问第二个元素只需要将指针向后移动四个字节
p++;

cout << *p << endl;

那么如何通过指针来遍历数组呢?

int arr1[5] = {1,2,3,4,5};
int* p1 = arr1;
for(int i = 0;i < sizeof(arr1) / sizeof(arr1[0]);i++){
cout << *p1++ << endl;

}

指针与函数

这种方式来对a,b进行交换,交换的是形参a和b,实参a b的值并没有修改

void swap01(int a,int b){
int temp = a;
a = b;
b = temp;
}
int main(){
int a = 20;
int b = 10;
swap01(a,b);
cout << "a = " << a << endl; //20
cout << "b = " << b << endl; //10

return 0;
}

我们可以借助指针来对a b的地址进行交换,从而达到交换实参值的效果

//传入进来的是地址
void swap02(int* a,int* b){
int temp = *a;
*a = *b;
*b = temp;
}
int main(){
int a = 20;
int b = 10;
swap02(&a,&b);
cout << "a = " << a << endl; //10
cout << "b = " << b << endl; //20

return 0;
}

指针函数和数组

借助指针和函数,打印冒泡排序

void printArr(int* arr,int len){
for(int i = 0;i < len;i++){
cout << arr[i] << endl;
}
}


void Bubble(int* arr,int len){
for(int i = 0;i < len;i++){
for(int j = 0;j < len - i - 1;j++){
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}

int main(){
int arr[] = {9,8,7,6,5,4,3,2,1,0};
int len = sizeof(arr) / sizeof(arr[0]);
Bubble(arr,len);
printArr(arr,len);
return 0;
}

结构体

可以自定义数据类型

定义结构体

struct Student{
int age;
string name;
string sex;
};

访问结构体(第一种方式)

struct Student s1;
//创建结构体的变量,通过这个变量来对属性进行访问和修改
s1.age = 12;
s1.name = "xiaohong";
s1.sex = "female";
cout << "age = " << s1.age << " name = " << s1.name << " sex = " << s1.sex;

访问结构体(第二种方式)

struct Student s2 = {20,"xiaoming","male"};
cout << "age = " << s2.age << " name = " << s2.name << " sex = " << s2.sex;

访问结构体(第三种方式不常用)

struct Student{
int age;
string name;
string sex;
}s3;

s3.age = 1000;
s3.name = "销量"
s3.sex = "none"

结构体数组

  • 定义结构体

    //定义结构体
    struct Student{
    int age;
    string name;
    string sex;
    };
  • 定义结构体数组

    Student stu[3] = {
    {1,"a","male"},
    {2,"b","female"},
    {3,"c","male"}
    };
  • 将结构体数组中某个元素的属性进行修改

    //给结构体数组中某一位赋值
    stu[0].age = 2;
    stu[0].name = "xiaoming";
    stu[0].sex = "female";
  • 遍历结构体数组

    //遍历结构体数组
    for(int i = 0;i < 3;i++){
    cout << " age = " << stu[i].age << " name = " << stu[i].name << " sex = " << stu[i].sex << endl;
    }

结构体指针

  • 定义结构体

    //定义结构体
    struct Student{
    int age;
    string name;
    string sex;
    };
  • 定义指针指向结构体变量地址

    Student s = {1,"sdf","female"};

    //通过指针来访问结构体变量
    Student* p = &s;

  • 借助结构体指针访问内部属性

    //通过指针->的形式来访问里面的属性
    cout << " age = " << p->age << " name = " << p->name << " sex = " << p->sex;

结构体嵌套

定义结构体

//定义结构体
struct Student{
int age;
string name;
string sex;
};

//定义结构体
struct Teacher{
int age;
string name;
string sex;
Student stu; //teacher结构体中内涵一个student结构体
};

借助teacher来访问student的属性

Teacher t;
t.age = 20;
t.stu.age = 30;
t.stu.sex = "female";

结构体作为函数参数

  • 结构体是作为值进行传递的

    //值传递
    void PrintStudentMsg(Student s){
    s.age = 100;
    cout << " 年龄 " << s.age << " 性别 " << s.sex << " 名字 " << s.name << endl;
    }
    int main(){
    Student s = {"zhangsan",20,"male"};
    PrintStudentMsg(s);
    //值并不发生修改
    cout << " 年龄 " << s.age << " 性别 " << s.sex << " 名字 " << s.name << endl;

    return 0;
    }

    在这里面修改的是形参的age属性,实参不受任何影响

  • 结构体作为地址传递

    //地址传递
    void PrintStudentMsg2(Student* s){
    s->name = "hello";
    cout << " 年龄 " << s->age << " 性别 " << s->sex << " 名字 " << s->name << endl;
    }
    int main(){
    Student s = {"zhangsan",20,"male"};

    Student* p = &s;
    PrintStudentMsg2(p);

    cout << " 年龄 " << s.age << " 性别 " << s.sex << " 名字 " << s.name << endl;

    return 0;
    }

    这里修改了实参的name属性

const修饰结构体

有效的防止了对结构体中的数据进行误操作

void PrintStuMsg(const Student* s){
//s->age = 100; //报错
cout << " age = " << s->age << " name = " << s->name << " sex = " << s->sex << endl;
}

案例一

要求创建三个老师有各自的姓名,并且分配给每一个老师5个学生,每一个学生有自己的名字和分数,要求分数随机

思路分析

  1. 创建老师的结构体,学生的结构体
  2. 关联老师和学生,通过在Teacher结构体中放置学生的结构体数组[5]
  3. 为每一个老师和学生添加名字
  4. 通过随机数为每一个学生添加随机分数,这个随机分数此时为静态分数
  5. 创建随机数种子,使随机数随机起来
  6. 打印每一个老师,及其带领的学生的信息
#include<bits/stdc++.h>
using namespace std;

struct Student{
string name;
int score;
};

struct Teacher{
string name;
Student stuArr[5];
};

void allocate(Teacher tArray[],int len){
string str = "ABCDEF";
for(int i = 0;i < len;i++){
tArray[i].name = "Teacher_";
tArray[i].name += str[i];
//给每一个老师的每一个学生进行赋值操作
for(int j = 0;j < 5;j++){
tArray[i].stuArr[j].name = "Student_";
tArray[i].stuArr[j].name += str[j];
int random = rand() % 61 + 40; //40 ~ 100 之间的数据
tArray[i].stuArr[j].score = random;
}

}
}

void PrintInfo(Teacher tArr[],int len){
for(int i = 0;i < len;i++){
cout << " TeacherName = " << tArr[i].name << endl;

for(int j = 0;j < 5;j++){

cout << "\t学生姓名 = " << tArr[i].stuArr[j].name << " 学生分数 = " << tArr[i].stuArr[j].score << endl;
}
}
}

int main(){
//创建随机数种子 实现真正随机的效果
srand((unsigned int)time(NULL));

//创建一个三名老师的数组
Teacher tArr[3];

int len = sizeof(tArr) / sizeof(tArr[0]);

allocate(tArr,len);


PrintInfo(tArr,len);

return 0;
}

案例二

给出一组英雄信息数据,要求通过每一个英雄的年龄进行排序

思路

  1. 创建英雄结构体
  2. 冒泡排序
  3. 打印信息
#include <bits/stdc++.h>
using namespace std;

struct Hero{
string name;
int age;
string sex;
};



void Bubble(Hero h[],int len){
for(int i = 0;i < len - 1;i++){
for(int j = 0;j < len - i - 1;j++){
if(h[j].age > h[j + 1].age){
Hero temp = h[j];
h[j] = h[j + 1];
h[j + 1] = temp;
}
}
}
}


void printHero(Hero h[],int len){
for(int i = 0;i < len;i++){
cout << " name = " << h[i].name << " age = " << h[i].age << " sex = " << h[i].sex << endl;
}
}



int main(){

Hero h[5] = {
{"刘备",50,"男"},
{"吕布",39,"男"},
{"张飞",34,"男"},
{"貂蝉",19,"女"},
{"蔡文姬",20,"女"}
};
int len = sizeof(h) / sizeof(h[0]);

Bubble(h,len);


printHero(h,len);


return 0;
}

通讯录管理系统

操作页面

void showMenu(){
cout << "***** ***** ***** ******" << endl;
cout << "***** 1.添加联系人 ***** " << endl;
cout << "***** 2.显示联系人 ***** " << endl;
cout << "***** 3.删除联系人 ***** " << endl;
cout << "***** 4.查找联系人 ***** " << endl;
cout << "***** 5.修改联系人 ***** " << endl;
cout << "***** 6.清空联系人 ***** " << endl;
cout << "***** 0.退出通讯录 ***** " << endl;
cout << "***** ***** ***** ******" << endl;
}

初始化联系人结构体

struct Person{
string m_Name;
string m_Sex;
int m_Age;
string m_Phone;
string m_Addr;
};

初始化通讯录结构体

#define MAX 1000
struct AddressBook{
//通讯录中保存的人员数组
Person perArray[MAX];

//通讯录中人员的大小
int m_Size;

};

退出系统

while(true){
showMenu();
cin >> select;
switch(select){
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
case 6:
break;
case 0:
cout << "欢迎下次使用" << endl;
return 0;
break;
default:
break;
}

添加联系人

//需要通过指针来修改实际参数
void addPerson(AddressBook* abs){
if(abs->m_Size == MAX){
cout << "通讯录名额已满,无法加入";

return;
}else {
//添加姓名
string name;
cout << "请输入你的姓名" << endl;
cin >> name;
abs->perArray[abs->m_Size].m_Name = name;

//添加年龄
int age;
cout << "请输入你的年龄" << endl;
cin >> age;
abs->perArray[abs->m_Size].m_Age = age;

//添加性别
int sex;
cout << "请输入你的性别" << endl;

while(true){
cout << "1 -> 男 " << " 2 -> 女" << endl;
cin >> sex;
if(sex == 1) {
abs->perArray[abs->m_Size].m_Sex = "男";
break;
}else if(sex == 2){
abs->perArray[abs->m_Size].m_Sex = "女";
}

cout << "输入有误重新输入" << endl;
}


//添加电话号码
string phone;
cout << "请输入你的电话号码" << endl;
cin >> phone;
abs->perArray[abs->m_Size].m_Phone = phone;

//添加地址
string addr;
cout << "请输入你的地址" << endl;
cin >> addr;
abs->perArray[abs->m_Size].m_Addr = addr;

abs->m_Size++;

cout << "添加成功" << endl;

system("clear");
//window里是cls


}
}
int main(){
//初始化操作
AddressBook abs;

abs.m_Size = 0;
}

显示联系人

遍历通讯录数组

void showPerson(AddressBook* abs){
int size = abs->m_Size;
if(size == 0){
cout << "通讯录中没有人" << endl;
}else {
for(int i = 0;i < size;i++){
cout << " name = " << abs->perArray[i].m_Name << " age = " << abs->perArray[i].m_Age << " sex = " << abs->perArray[i].m_Sex << " phone = " << abs->perArray[i].m_Phone << " addr = " << abs->perArray[i].m_Addr << endl;
}
}

}

删除联系人

数组前移,长度减去1

int isExist(AddressBook* abs,string name){
for(int i = 0;i < abs->m_Size;i++){
if(abs->perArray[i].m_Name == name){
return i;
}
}
return -1;
}

void deletePerson(AddressBook* abs){
cout << "请输入你要删除的联系人" << endl;
string name;
cin >> name;
int res = isExist(abs,name);
if(res != -1){
for(int i = 0;i < abs->m_Size;i++){
//将后面的数据前移
abs->perArray[i] = abs->perArray[i + 1];
}
abs->m_Size--;
cout << "删除成功" << endl;
}else {
cout << "没有你要找的人" << endl;
}
}

查找联系人

void findPerson(AddressBook* abs){
cout << "请输入你想查找的人" << endl;
string name;
cin >> name;
int res = isExist(abs,name);

if(res != -1){
cout << " name = " << abs->perArray[res].m_Name << " age = " << abs->perArray[res].m_Age << " sex = " << abs->perArray[res].m_Sex << " phone " << abs->perArray[res].m_Phone << " addr = " << abs->perArray[res].m_Addr;
}else {
cout << "查无此人" << endl;
}

}

修改联系人

查找修改人的下标,对每一个属性进行修改操作

void modifyPerson(AddressBook* abs){
cout << "请输入你修改的人" << endl;
string name;
cin >> name;
int res = isExist(abs,name);
if(res != -1){
cout << "请输入修改的名字" << endl;
string newName;
cin >> newName;
abs->perArray[res].m_Name = newName;


cout << "请输入修改的年龄" << endl;
int newAge;
cin >> newAge;
abs->perArray[res].m_Age = newAge;

cout << "请输入修改的性别" << endl;

while(true){
cout << " 1 -> 男 " << " 2 -> 女 " << endl;
int newSex;
cin >> newSex;
if(newSex == 1){
abs->perArray[res].m_Sex = "男";
break;
}else if(newSex == 2){
abs->perArray[res].m_Sex = "女";
break;
}
cout << "输入错误请重新输入" << endl;
}


cout << "请输入修改的电话" << endl;
string newPhone;
cin >> newPhone;
abs->perArray[res].m_Phone = newPhone;

cout << "请输入修改的地址" << endl;
string newAddr;
cin >> newAddr;
abs->perArray[res].m_Addr = newAddr;
}else {
cout << "查无此人" << endl;
}
}

清空联系人

清空联系人本质并没有将人清掉,只不过是遍历不到了而已,下一次添加数据把之前残留的数据进行了覆盖而已

void clearPerson(AddressBook* abs){
abs->m_Size = 0;
cout << "通讯录清空成功" << endl;
}

完整代码

#include <bits/stdc++.h>
#include <iostream>
using namespace std;

#define MAX 1000

struct Person{
string m_Name;
string m_Sex;
int m_Age;
string m_Phone;
string m_Addr;
};


struct AddressBook{
//通讯录中保存的人员数组
Person perArray[MAX];

//通讯录中人员的大小
int m_Size;

};



void showMenu(){
cout << "***** ***** ***** ******" << endl;
cout << "***** 1.添加联系人 ***** " << endl;
cout << "***** 2.显示联系人 ***** " << endl;
cout << "***** 3.删除联系人 ***** " << endl;
cout << "***** 4.查找联系人 ***** " << endl;
cout << "***** 5.修改联系人 ***** " << endl;
cout << "***** 6.清空联系人 ***** " << endl;
cout << "***** 0.退出通讯录 ***** " << endl;
cout << "***** ***** ***** ******" << endl;
}


void addPerson(AddressBook* abs){
if(abs->m_Size == MAX){
cout << "通讯录名额已满,无法加入";

return;
}else {
//添加姓名
string name;
cout << "请输入你的姓名" << endl;
cin >> name;
abs->perArray[abs->m_Size].m_Name = name;

//添加年龄
int age;
cout << "请输入你的年龄" << endl;
cin >> age;
abs->perArray[abs->m_Size].m_Age = age;

//添加性别
int sex;
cout << "请输入你的性别" << endl;

while(true){
cout << "1 -> 男 " << " 2 -> 女" << endl;
cin >> sex;
if(sex == 1) {
abs->perArray[abs->m_Size].m_Sex = "男";
break;
}else if(sex == 2){
abs->perArray[abs->m_Size].m_Sex = "女";
}

cout << "输入有误重新输入" << endl;
}


//添加电话号码
string phone;
cout << "请输入你的电话号码" << endl;
cin >> phone;
abs->perArray[abs->m_Size].m_Phone = phone;

//添加地址
string addr;
cout << "请输入你的地址" << endl;
cin >> addr;
abs->perArray[abs->m_Size].m_Addr = addr;

abs->m_Size++;

cout << "添加成功" << endl;



}
}
void showPerson(AddressBook* abs){
int size = abs->m_Size;
if(size == 0){
cout << "通讯录中没有人" << endl;
}else {
for(int i = 0;i < size;i++){
cout << " name = " << abs->perArray[i].m_Name << " age = " << abs->perArray[i].m_Age << " sex = " << abs->perArray[i].m_Sex << " phone = " << abs->perArray[i].m_Phone << " addr = " << abs->perArray[i].m_Addr << endl;
}
}

}

int isExist(AddressBook* abs,string name){
for(int i = 0;i < abs->m_Size;i++){
if(abs->perArray[i].m_Name == name){
return i;
}
}
return -1;
}

void deletePerson(AddressBook* abs){
cout << "请输入你要删除的联系人" << endl;
string name;
cin >> name;
int res = isExist(abs,name);
if(res != -1){
for(int i = 0;i < abs->m_Size;i++){
//将后面的数据前移
abs->perArray[i] = abs->perArray[i + 1];
}
abs->m_Size--;
cout << "删除成功" << endl;
}else {
cout << "没有你要找的人" << endl;
}
}

void findPerson(AddressBook* abs){
cout << "请输入你想查找的人" << endl;
string name;
cin >> name;
int res = isExist(abs,name);

if(res != -1){
cout << " name = " << abs->perArray[res].m_Name << " age = " << abs->perArray[res].m_Age << " sex = " << abs->perArray[res].m_Sex << " phone " << abs->perArray[res].m_Phone << " addr = " << abs->perArray[res].m_Addr;
}else {
cout << "查无此人" << endl;
}

}


void modifyPerson(AddressBook* abs){
cout << "请输入你修改的人" << endl;
string name;
cin >> name;
int res = isExist(abs,name);
if(res != -1){
cout << "请输入修改的名字" << endl;
string newName;
cin >> newName;
abs->perArray[res].m_Name = newName;


cout << "请输入修改的年龄" << endl;
int newAge;
cin >> newAge;
abs->perArray[res].m_Age = newAge;

cout << "请输入修改的性别" << endl;

while(true){
cout << " 1 -> 男 " << " 2 -> 女 " << endl;
int newSex;
cin >> newSex;
if(newSex == 1){
abs->perArray[res].m_Sex = "男";
break;
}else if(newSex == 2){
abs->perArray[res].m_Sex = "女";
break;
}
cout << "输入错误请重新输入" << endl;
}


cout << "请输入修改的电话" << endl;
string newPhone;
cin >> newPhone;
abs->perArray[res].m_Phone = newPhone;

cout << "请输入修改的地址" << endl;
string newAddr;
cin >> newAddr;
abs->perArray[res].m_Addr = newAddr;
}else {
cout << "查无此人" << endl;
}
}

void clearPerson(AddressBook* abs){
abs->m_Size = 0;
cout << "通讯录清空成功" << endl;
}
int main(){
AddressBook abs;
abs.m_Size = 0;
int select;
while(true){
showMenu();
cin >> select;
switch(select){
case 1:
addPerson(&abs);
break;
case 2:
showPerson(&abs);
break;
case 3:
// {
// cout << "请输入删除联系人的姓名";
// string rname;
// cin >> rname;

// if(isExist(&abs,rname) == -1){
// cout << "没找到此人" << endl;
// }else {

// }
// break;
deletePerson(&abs);
break;
// }
case 4:
findPerson(&abs);
break;
case 5:
modifyPerson(&abs);
break;
case 6:
clearPerson(&abs);
break;
case 0:
cout << "欢迎下次使用" << endl;
return 0;
break;
default:
break;
}
}
return 0;
}

C++核心

内存四区

  • 代码区

    存放CPU执行的机器指令

    **特点:**代码区是共享的,代码区是只读的

  • 全局区

    存放的变量:全局变量,静态变量,全局常量,字符串常量

    通过打印可知全局变量和局部变量所在的区域不一样

  • 栈区

    由编译器管理内存的释放

    局部变量存放于栈区,当函数执行玩后栈区的数据就会被释放,所以不要返回局部变量的内存地址

  • 堆区

    由程序员管理的内存

    **如何创建: **借助new 数据类型(传入的值)在堆区中开辟一段内存空间,通过解引用的方式来进行访问,函数调用后不会自动释放内存

    **如何创建一个数组: *int p = new int[10];

    如何释放: delete + 内存地址 手动释放内存,释放数组delete[] + 地址

引用

通过引用我们可以让一个变量指向另一个变量的内存地址,从而操作同一块内存空间

int a = 10;
int &b = a;
b = 20;
cout << a << endl; //20
cout << b << endl; //20

注意事项

  • 引用不能只声明,不指向
  • 引用一旦初始化后就无法进行修改

引用作为函数的参数

可以做到形参修饰实参的效果

void swap(int &a,int &b){
int temp = a;
a = b;
b = temp;
}

引用作为函数的返回值

  • 不要返回局部变量的引用

  • 函数的返回如果是引用的话可以作为左值去使用

    int& func(){
    static int a = 20;
    return a;

    }
    int main(){

    int &fuc1 = func();
    cout << fuc1 << endl;

    func() = 100;
    cout << fuc1 << endl;

    }

引用作为指针常量

int main(){
int a = 20;
int &b = a;
b = 20;
cout << a << endl; //20
cout << b << endl; //20
return 0;
}

const修饰引用

主要用于防止函数不小心进行修改

void showTime(const int& val){
// val = 100; const修饰的引用无法被修改
cout << val << endl;
}
int main(){

const int& ref = 10; //int temp = 10; const int& ref = temp;
int a = 10;
showTime(a);

cout << a << endl;
}

函数高级

默认参数

函数参数可以设置成默认值

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

在这个案例中如果你传入了三个参数,默认值将被你传入的参数取代掉

函数可以提前进行声明操作

int getSum(int a,int b,int c);

注意

  • 设置默认值的参数后面必须都设置默认值
  • 声明和实现只能有一个有默认参数

占位参数

void getSum(int a,int ){
cout << "Hello world" << endl;
}

占位参数是可以有默认值的

void getSum(int a,int  = 10){
cout << "Hello world" << endl;
}

函数重载

什么事函数重载?

多个函数方法名一致,参数不同,类型不同,顺序不同,调用时根据参数的不同执行不同的函数

void sayHello(){
cout << "Hello" << endl;
}

void sayHello(int a){
cout << a << endl;
}

void sayHello(double s){
cout << s << endl;
}


void sayHello(int a,double s){
cout << s << endl;
}

void sayHello(double a,int s){
cout << s << endl;
}

注意事项

引用重载

void sayHello(int &a){ //int &a = 10不合法
cout << "Hello world1" << endl;
}

void sayHello(const int &a){ //const int &a = 10 合法
cout << "Hello world2" << endl;
}

int main(){
//a是变量走第一条语句
int a = 10;
sayHello(a); // Hello world1

sayHello(10); //Hello world2

return 0;
}

尽量在重载的时候不要给参数设置默认值

类和对象

什么是类呢?

每个人都有自己的特征,同理其他物质也有自己的属性,我们可以将不同特征的人作为一个类,物质也可以作为一个类

C++面向对象的三大特性

  • 封装
  • 继承
  • 多态

封装

创建一个计算圆周长的类

const double PI = 3.14;
class Circle{
public: //公共权限

int c_dis; //半径
double calZC() {
return 2 * c_dis * PI;
}
};

如何使用这个类呢?

int main(){

Circle cc; //通过类创建对象
cc.c_dis = 10; //为对象的属性赋值
cout << cc.calZC() << endl; 调用类的方法

return 0;

案例:设计一个学生类,每一个学生有自己的姓名和年龄,我们需要调用这个类中的show方法对学生的信息进行显示

#include <bits/stdc++.h>
using namespace std;


class Stu{
public:
string name;
int age;
void show(){
cout << name << ":" << age << endl;
return;
}
};


int main(){

Stu stu;
stu.name = "张三";
stu.age = 12;

stu.show();

return 0;
}

当然,类中的方法其实也是可以对属性进行修改的

假如我们想要对名字进行修改

除了

stu.name = "张三"

还可以

void gvName(string n) {
name = n;
}

类中的属性被称为成员属性,类中的方法被称为成员方法

访问权限

  • 公共访问权限:类里面可以访问,类外面也可以访问
  • 私有访问权限:类里面可以访问,外面不可以访问
  • 保护访问权限:类里面可以访问,外面不可以访问
#include <bits/stdc++.h>
using namespace std;


class Peo{
public:
string name;
private:
int age;
protected:
char sex;

public:
void set(){
name = "张三";
age = 29;
sex = '6';
}
};


int main(){

Peo peo;
peo.name = "张三";
// peo.age = 29; 报错
// peo.sex = '3'; 报错


return 0;
}

通过对类的介绍我们会发现类和结构体十分的相似

那么他们的区别是什么呢?

类的默认访问权限是private,而结构体默认是public

实际开发中成员属性设置为私有,通过成员方法进行修改和访问

class Peo{
string name;
int age;
string m_lover;

public:
void setName(string n) {
name = n;
}

string getName() {
return name;
}
//只读age
int getAge() {
age = 0;
return age;
}

//只写lover
void set_lover(string s) {
m_lover = s;
}

};

对象的初始化和清理

构造函数

书写格式

  • 构造函数不用写返回值(包含void)
  • 构造函数名字必须与类名保持一致
  • 可以有参数,可以发生重载现象
  • 构造函数在对象创建好后就会被自动调用
class Person{
public:
Person(){
cout << "hello" << endl;
}
};

析构函数

  • 不用写返回值
  • 名和类名一致,名称前需要加~
  • 没有参数,无法发生重载现象
  • 对象销毁的时候会自动调用
class Person{
public:
Person(){
cout << "hello" << endl;
}

~Person(){
cout << "dead program" << endl;
}
};

当创建对象的时候打印”hello”,当栈内存释放的时候,打印dead Program

构造函数的分类

根据有无参数,我们可以分为两类,一类是有参数构造函数,还有一类是无参数构造函数

class Person{
public:
Person(){
cout << "hello" << endl;
}

Person(int a){
cout << "hello1" << endl;
}
}

拷贝构造函数(将某一个对象身上的属性施加到当前对象身上)

class Person{
int age;
public:
Person(){
cout << "hello" << endl;
}

~Person(){
cout << "dead program" << endl;
}
//const的目的是避免修改原来的构造函数
Person(const Person &b) {
age = b.age;
}
};

如何触发有参构造

Person p(19);

如何触发拷贝构造

Person p1;
Person p2(p1);

注意事项:在调用默认构造函数的时候不要加(),否则可能会被认为是函数声明

显示类

针对于上面创建出来的对象,我们发挥对象应有的价值

Person p; //无参构造
Person p1 = Person(10);
Person p2 = Person(p1);

Person(p2); //报错 编译器会认为是对p2的重定义

在这里Person(10)是匿名对象,在对象创建好后就释放掉,所以我们需要为其命名

不要使用拷贝构造初始化匿名构造

隐式转换法

Person p = 10; //相当于是Person p(10);
Person p1 = p;

将类作为值进行传递,实则调用了一次拷贝构造

class Person{
int age;
public:
Person(){
cout << "无参数构造" << endl;
}

~Person(){
cout << "dead program" << endl;
}
Person(int n) {
age = n;
cout << "有参数构造" << endl;
}

Person(const Person &b) {
age = b.age;
cout << "拷贝构造" << endl;
}
};

void doWork(Person t) {

}

void st() {
Person p;
//显示无参数构造

doWork(p);
//传递p的同时显示拷贝构造
}

int main(){

st();


return 0;
}

以值方式返回局部对象

Person doWork1(){
Person p2;

cout << (int*)&p2 << endl;
//由于是以值的形式进行返回的,返回的结果是p2的拷贝,此时会触发拷贝构造
return p2;
}


void st1() {
Person p1 = doWork1();
cout << (int*)&p1 << endl;
}

现今版本应该是优化过的返回并没有触发拷贝构造