笔者写在前面
毕业了,机械狗太无聊
把以前忘光光的捡起来继续学,从小白开始
慢慢完善c++的笔记。写到哪里是哪里,前后会慢慢补充,格式有点乱,读者勿嗔
至于学到什么深度,最后要做什么暂时不考虑
为了学会而学会?
希望帮助到大家,也希望批评指正,一起进步
20211024
指针
1、引入内存理解
2、指针定义
3、指针大小
4、空指针nullptr
5、野指针
6、const和指针
- const修饰指针 —(可以换女朋友(地址)但女朋友名字不可以变(值))
- const修饰值(此时就是常量了) —(女朋友的名字可以改)
- const即修饰指针,又修饰值(常量)
int a = 10;
int* const p = &a;//修饰a的地址,地址不可换
//const int* p = &a;//修饰a的值,值不可换
cout << p << endl;
cout << *p << endl;
int b=11;
//p = &b;//换b的地址(通过换地址换值)
*p = b;//换b的值
cout << p << endl;
cout << *p << endl;
指针和数组——指针访问数组
double arr[] = { 1,23,4,34,53,5,46,45,64,6,23,423,42 };
double* p =&arr[0];//初始化指向首地址
//以下帮助理解
cout << sizeof(arr) << endl;//104
cout << sizeof(arr[0]) << endl;//8
cout << sizeof(double) << endl;//8
cout << sizeof(double*) << endl;//4,指针的大小取决于编译器的位数,此处是32位编译器,4字节
cout << sizeof(p) << endl;//4
cout << endl;
for (int i = 0; i < 20; i++)
{
cout << *p << endl;
p++;//地址递增相应字节大小(double是8个字节)
}
指针和函数——值传递和地址传递
结构体:自定义的数据类型
结构体定义:struct 结构体名 { 结构体成员列表 };
其中:结构体成员列表=数据类型1 成员名1;数据类型2 成员名2;
创建结构体变量:(struct可以省略)
1、struct 结构体名 结构体变量名;(不初始化,成员单独初始化)
成员单独初始化:结构体变量名.结构体成员名=初始值;
2、struct 结构体名 结构体变量名={初始值};(初始化)
3、定义结构体时顺便创建结构体变量(少用,看得懂就行)
struct 结构体名 { 结构体成员列表 }结构体变量名;(不初始化)
struct 结构体名 { 结构体成员列表 }结构体变量名={初始值};(初始化)
//结构体定义
struct student
{
//成员列表
string name; //姓名
int age; //年龄
}stu3; //结构体变量创建方式3
int main() {
//结构体变量创建方式1
struct student stu1; //struct 关键字可以省略
stu1.name = "张三";
stu1.age = 18;
cout << "姓名:" << stu1.name << " 年龄:" << stu1.age << endl;
//结构体变量创建方式2
struct student stu2 = { "李四",19,60 };
cout << "姓名:" << stu2.name << " 年龄:" << stu2.age << endl;
//结构体变量创建方式3 ,单独初始化结构体
stu3.name = "王五";
stu3.age = 18;
stu3.score = 80;
cout << "姓名:" << stu3.name << " 年龄:" << stu3.age << endl;
system("pause");
return 0;
}
结构体数组:
struct ss {
string name;
int age;
};
ss ss_arr[2] = {
{"her",23},
{"he",21}
};
for (int i = 0; i < 2; i++)
{
cout<<"name: " << ss_arr[i].name << "\t"<<"age: " << ss_arr[i].age << endl;
}
结构体指针
理解1:
//定义结构体
struct ss{
string name;
int age;
};
//创建结构体变量
ss her = { "her",23};
ss he = { "he", 21 };
//打印结构体变量的内容(成员信息)
cout << he.name<< "\t" << he.age << endl;
cout << her.name << "\t" << her.age << endl;
//通过指针访问
struct ss* p1 = &her;
string *p2 = &her.name;
cout << p1 << endl;
cout << p2 << endl;
理解2:
代码对称,方便理解
struct ss { string name; int age; }s1;//不初始化
s1 = { "s1", 17 };//单独初始化
sturct ss* p = &s1;//struct可以省略
/*ss*/ string* p2 = &s1.name;
嵌套结构体
代码对称,方便理解
//定义学生结构体
struct stduents
{
string name;
int age;
};
//定义老师结构体
struct teachers
{
string name;//字符串变量名,字符串变量名
int age;//整型类型名,整型变量名
struct stduents s1;//struct(可以省略) students是结构体类型,s1是结构体变量名(老师的学生s1)
};
//创建学生结构体变量
stduents s1 = { "bill",18 };//students是结构体类型,s1是结构体变量名
//创建老师结构体变量
teachers t1 = { "herry",23,s1 };
cout << t1.name << endl;
cout << t1.age << endl;
cout << t1.s1.name <<" " << t1.s1.age << endl;
多个学生则可以把学生结构体换成结构体数组,如下
试了一下,暂时不会
//定义学生结构体
struct stduents
{
string name;
int age;
};
//定义老师结构体
struct teachers
{
string name;//字符串变量名,字符串变量名
int age;//整型类型名,整型变量名
struct stduents ss[];//struct students是结构体类型,ss[2]是结构体变量名(老师的两个学生ss[0]和ss[1])
};
//创建学生结构体变量
stduents ss[2] = { "bill",18,"ada",19};//students是结构体类型,s1是结构体变量名
//创建老师结构体变量
teachers t1 = { "herry",23,ss[2] };
cout << t1.name << endl;
cout << t1.age << endl;
cout << t1.ss[0].name <<" " << t1.ss[0].age << endl;
cout << t1.ss[1].name <<" " << t1.ss[1].age << endl;
补充1:
err:c++中"cout"不明确问题
ans:将using namespace std 删除后重新输入一遍
补充2:
qus:什么是析构函数?
ans:
结构体作为函数参数
值传递
//定义学生结构体
struct stduents
{
string name;
int age;
};
void printstudents(struct stduents ss){
cout << ss.name <<" " << ss.age << endl;
}
int main() {
//创建学生结构体变量
struct stduents ss1 = { "bill",18 };//students是结构体类型,ss1是结构体变量名
struct stduents ss2;
ss2.name = "ada";
ss2.age = 19;
printstudents(ss1);
printstudents(ss2);
}
地址传递
//定义学生结构体
struct stduents
{
string name;
int age;
};
void printstudents(struct stduents* ss_p){
cout <<ss_p->name <<" " << ss_p->age << endl;//用->来访问地址中结构体的成员
}
int main() {
//创建学生结构体变量
struct stduents ss1 = { "bill",18 };//students是结构体类型,ss1是结构体变量名
struct stduents ss2;
ss2.name = "ada";
ss2.age = 19;
printstudents(&ss1);
printstudents(&ss2);
}
地址传递实参的值会在调用函数内部被拦截修改
//定义学生结构体
struct stduents
{
string name;
int age;
};
void printstudents(struct stduents* ss_p){
ss_p->age = 100;//拦截修改年龄为100
cout <<ss_p->name <<" " << ss_p->age << endl;//用->来访问地址中结构体的成员
}
int main() {
//创建学生结构体变量
struct stduents ss1 = { "bill",18 };//students是结构体类型,ss1是结构体变量名
struct stduents ss2;
ss2.name = "ada";
ss2.age = 19;
//调用函数打印
printstudents(&ss1);
printstudents(&ss2);
cout << endl;
//直接打印
cout << ss1.name << " " << ss1.age << endl;//年龄变成100
}
c++面向对象编程(oop)
vs设置:
qus1:编辑器
ans:设置方式:工具 – 选项 – 环境 – 字体和颜色 – 字体/大小
程序运行过程中的内存区
代码
全局
栈
堆
乱七八糟的量
基本:
区别,调用函数的参数为实参,定义函数的参数为形参(实参传到形参为函数的入口,调用函数接收返回值为函数的出口)
变量
静态变量
全局变量:函数体外定义。程序的所在部分(甚至其它文件中的代码)都可以使用。
局部变量:函数体内(自定义函数和main函数)。出现在一个作用域内,它们是局限于一个函数的。局部变量默认为auto(自动变量)。
字面常量
const常量(常变量)
关键字
extern
auto自动变量
register寄存器变量,是一种局部变量。
static静态变量,类型引用,静态初始值默认为0
volatile变量
const常量,实例引用
暂存一下,要修改
//全局变量
int g_a = 10;
int g_b = 20;
int main() {
//局部变量
int a = 10;
int b = 10;
cout << (int)&a << endl;
cout << (int)&b << endl;
cout << endl;
//静态变量
static int s_a = 10;
static int s_b = 10;
cout << (int)&s_a << endl;
cout << (int)&s_b << endl;
cout << endl;
//面值常量:字符串常量
cout << (int)&"hello" << endl;
cout << endl;
//const常量
//打印全局变量
cout << (int)&g_a << endl;
cout << (int)&g_b << endl;
cout << endl;
全局区
静态变量
字面常量:字符串常量
全局常量
全部变量
栈区:栈区数据由编译器开辟和释放,不要返回栈区数据的地址
局部常量
局部变量
函数参数(形参)
局部变量(栈区)的地址会变动(vs2019中会自动固定,但值不一定固定)
int* func() {
int num=10;
return #
}
int main() {
int a = 1;
int b = 2;
int *p=func();
//多次打印
cout << p << endl;
cout << *p << endl;
cout << p << endl;
cout << *p << endl;
cout << p << endl;
cout << *p << endl;
cout << p << endl;
cout << *p << endl;
}
堆区
在堆区创建数据:关键字new
释放堆区内存:程序结束;删除堆中数据的地址:delete p;
堆区创建数据
int* func() {
int* p = new int(10);//堆区整型变量10,地址保存在指针p中
return p;
}
int main() {
int *c=func();
cout << c << endl;//打印地址
cout << *c << endl;//打印值
}
堆区创建数组
//在堆区创建数组
int* func1() {
int* p = new int[10];//堆区整型数组变量,有10个变量,(首)地址保存在指针p中
return p;
}
int main() {
int* arr = func1();
//给数组赋值
for (int i = 0; i < 10; i++)
{
arr[i] =i+10;
}
//打印数组
for (int i = 0; i <10; i++)
{
cout << arr[i] << endl;
cout << &arr[i] << endl;
}
//释放数组
delete[] arr;//[]表示释放数组
}
引用:给变量取别名
语法:int& b = a;
理解:
1、取个别名
2、本质&b=&a
3、b的地址也指向a(不好理解)
4、把a的地址传给b
5、地址一样,物理内存上就是一个
int a = 10;
cout << a << endl;
int& b = a;//取个别名,本质&b=&a,即b的地址也指向a,把a的地址传给b,地址一样,物理内存上就是一个块内存。
b = 20;
cout << a << endl;
cout << b << endl;
//1. 值传递,实参以值传入,在自定义函数中直接换值
void mySwap01(int a, int b) {
int temp = a;
a = b;
b = temp;
}
//2. 地址传递,实参以地址传入,在自定义函数中交换地址
void mySwap02(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
//3. 引用传递,实参以值传入,在自定义函数中(以别名作用)本质在交换地址
void mySwap03(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10;
int b = 20;
mySwap01(a, b);
cout << "a:" << a << " b:" << b << endl;
mySwap02(&a, &b);
cout << "a:" << a << " b:" << b << endl;
mySwap03(a, b);
cout << "a:" << a << " b:" << b << endl;
system("pause");
return 0;
}
引用作为函数返回值
int& test01() {
int a = 10;//局部变量在栈区
return a;
}
int& test02() {
static int a = 10;//静态变量在全局区
return a;
}
int main() {
int& ref1 = test01();
cout << ref1<< endl;//第一次编译器保留
cout << ref1 << endl;//后续编译器释放就不一定对到了
int& ref2 = test02();
cout << ref2 << endl;//10
cout << ref2 << endl;//10
cout << ref2 << endl;//10
cout << ref2 << endl;//10
cout << ref2 << endl;//10
cout << ref2 << endl;//10
cout << ref2 << endl;//10
cout << ref2 << endl;//10
cout << ref2 << endl;//10
cout << endl;
test02() = 1000;//函数的返回值是调用可以作为左值
cout << ref2<< endl;//1000
}
引用的本质:引用的本质在c++内部实现是一个指针常量
void func(int& ref){//发现ref是引用,转换为 int* const ref = &a;
ref = 100; // 发现ref是引用,自动转换:*ref = 100
}
int main(){
int a = 10;
int& ref = a; //发现ref是引用,自动转换:int* const ref = &a; 指针常量是指针指向不可改,也说明为什么引用不可更改
ref = 20; //发现ref是引用,自动转换为:*ref = 20;
cout << "a:" << a << endl;
cout << "ref:" << ref << endl;
func(a);
return 0;
}
常量引用:常量引用主要用来修饰形参,防止误操作
双const
函数高级
函数定义:
参数列表默认值
1、如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
2、调用时,有实参用实参,无实参用默认值
3、声明有默认参数,函数的实现就不能有默认参数
占位参数
函数重载
同一个作用域下,函数名称相同
编译器靠函数的参数(函数的入口)确定调用的函数,调用同名的哪一个函数取决于参数对应原则
函数参数类型不同 或者 个数不同 或者 顺序不同
函数的返回值不可以作为函数重载的条件
类型不同,const int 和int就是不同,可以发生函数重载
坑1:
引用作为函数重载的条件
坑2:
默认参数
oop
eg1:
const double PI = 3.14;
class circle
{
public:
int r;
double s() {
double s = PI * r * r;
return s;
}
double c() {
double c = 2 * PI * r;
return c;
}
};
int main() {
circle c1;
cin>> c1.r ;
double s = c1.s();
double c = c1.c();
cout << s << endl;
cout << c << endl;
system("pause");
return 0;
}
eg2:
class student
{
public:
//属性
string t_name;
int t_age;
//行为
//打印学生信息
void printstudents() {
cout << t_name <<" " << t_age << endl;
}
//创建成员方法设置名字
void get_name(string name) {
t_name = name;
}
//创建成员方法设置年龄
void get_age(int age) {
t_age = age;
}
};
int main() {
student s1;
//s1.t_name = "herry2";
//s1.t_age = 18;
s1.get_name ( "herry");
s1.get_age ( 18);
s1.printstudents();
}
三种权限
public:成员在类内可以访问,在类外可以访问
protected:成员在类内可以访问,在类外不能访问。子可以访问父保护权限
private:成员在类内可以访问,在类外不能访问。子不可以访问父保护权限