目录
一、指针
1.1 指针定义语法
数据类型 * 指针变量名
int a;
int * p;
p = &a;
1.2 使用指针
可以通过解引用的方式来找到指针指向的内存。
指针前加 * 代表解引用,找到指针指向的内存中的数据。
1.3 占用空间
在32位操作系统下:占用四个字节空间
在64位操作系统下:占用八个字节空间
1.4 空指针和野指针
空指针用于给指针变量进行初始化
空指针是不可以进行访问的
野指针:指针变量指向非法的内存空间
空指针和野指针都不是我们申请的空间,因此不要随意访问。
1.5 const修饰指针和常量
const修饰指针------常量指针
指针的指向可以修改,但是指针指向的指不可以改。
const int * p=&a;
const修饰常量------指针常量
指针的指向不可以改,但是指针指向的值可以改。
int * const p=&a;
const即修饰指针又修饰常量
指针的指向和指针指向的值都不可以改。
const int * const p=&a;
1.6 指针和数组
int main() {
//指针和数组
//利用指针访问数组中的元素
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
cout << "第一个元素为:" << arr[0] << endl;
int* p = arr; / /arr就是数组首地址
cout << "利用指针访问第一个元素:" << *p << endl;
p++;//让指针向后偏移4个字节
cout << "利用指针访问第二个元素:" << *p << endlsm
}
cout << ”利用指针遍历数组"<<endl;
int* p2 = arr;
for (int i = 0; i < 10; i++)
{
//cout << arr[i] << endl;
cout<<*p2<< end1;
p2++;
}
1.7 指针和函数
值传递
void swap01(int a,int b)
地址传递
void swap02(int *p1,int *p2)
二、结构体
语法:
struct 结构体名{
结构体成员列表
};
2.1 创建学生数据类型
struct Student{
string name; //姓名
int age; //年龄
int score; //分数
};
2.2 通过学生类型创建具体学生
(创建时struct 关键字可以省略,定义时不能)
struct Student S1; //Student S1 也可
S1.name = "张三";
S1.age = 18;
S1.score = 100;
struct Student S2 = {"李四",19,80};
//定义结构体时顺便创建结构变量
struct Student{
string name; //姓名
int age; //年龄
int score; //分数
}S3; //顺便创建结构体变量,S3就是结构体变量
S3.name = "张三";
S3.age = 18;
S3.score = 100;
2.3 结构体数组
语法: struct 结构体名 数组名{元素个数}={ {},{},...{}}
//1.定义结构体
struct Student{
string name; //姓名
int age; //年龄
int score; //分数
};
int main(){
//2.创建结构体数组
struct Student stuArray[3] = {
{"张三",18,100},
{"李四",28,99},
{"王五",38,166},
};
//3.给结构体数组中的元素赋值
stuArray[2].name = "赵六";
//4.遍历结构体数组
for(int i = 0 ; i < 3 ; i++)
{
cout <<"姓名:"<<stuArray[i].name
<<"年龄:"<<stuArray[i].age
<<"成绩:"<<stuArray[i].score<<endl;
}
}
2.4 结构体指针
利用操作符->可以通过结构体指针访问结构体属性
//1.定义结构体
struct Student{
string name; //姓名
int age; //年龄
int score; //分数
};
int main(){
//1.创建学生结构体变量
struct Student s = {"张三",18,100}; //struct可省略
//2.通过指针指向结构体变量
struct Student * p = &a; //struct可省略
//3.通过指针访问结构体变量中的数据
cout<<"姓名:"<< p->name <<"年龄:"<< p->age <<"成绩:"<< p->score;
}
2.5 结构体嵌套结构体
#include<iostream>
#include<string>
using namespace std;
//学生结构体
struct student {
string name;
int age;
int score;
};
//老师结构体
struct teacher {
int id;
string name;
int age;
struct student stu; //辅导的学生
};
int main()
{
teacher t;
t.age = 50;
t.id = 10000;
t.name = "老王";
t.stu.name = "小王";
t.stu.age = 18;
t.stu.score = 100;
cout << "老师姓名:" << t.name<<endl << "学生姓名:" << t.stu.name<<endl;
system("pause");
return 0;
}
2.6 结构体做函数参数
#include<iostream>
#include<string>
using namespace std;
//学生结构体
struct student {
string name;
int age;
int score;
};
void printstudent1(struct student s)// 值传递
{
cout << "子函数1中 姓名:" << s.name << endl;
}
void printstudent2(struct student* p)//地址传递
{
cout << "子函数2中 姓名:" << p->name << endl;
}
int main()
{
student s;
s.name = "张三";
s.age = 20;
s.score = 85;
printstudent1(s);
printstudent2(&s);
return 0;
}
2.7 结构体中const使用
void print(const student *p ) //p的值无法被修改,类似实参形参,不过指针只传递地址,参数要传输大量数据,节省了空间.加入const之后一旦有修改操作就会报错
三、内存
3.1 内存四区
C++程序在执行时,将内存大方向划分为4个区域:
·代码区:存放函数体的二进制代码,由操作系统进行管理的
·全局区:存放全局变量和静态变量以及常量
·栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
·堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
内存四区意义:
不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程
程序运行前:
代码区:存放函数体的的二进制代码,操作系统管理。
特点: ①共享 :对于频繁被执行的程序,只需要在内存中有一份代码即可。
②只读:目的是防止程序意外地修改了它的指令。
全局区:存放全局变量、静态变量(stati关键字)和常量(字符串常量和const修饰的全局变量(全局常量))。
不在全局区中:局部变量、const修饰的局部变量(局部常量)
//全局变量
int g_a = 10;
const int c_g_a = 10;
int main()
{
int l_a = 10;
//静态变量
static int s_a = 10;
//常量:字符串常量/const修饰的变量(全局/局部变量)
const char* a = "hello";
const int c_l_a = 10;
cout << "局部变量l_a的十进制地址为:" << (int)&l_a << endl;
cout << "全局变量g_a的十进制地址为:" << (int)&g_a << endl;
cout << "静态变量s_a的十进制地址为:" << (int)&s_a << endl;
cout << "字符串常量a的十进制地址为:" << (int)a << endl;
cout << "const修饰的全局变量c_g_a的十进制地址为:" << (int)&c_g_a << endl;
cout << "const修饰的局部变量c_l_a的十进制地址为:" << (int)&c_l_a << endl;
return 0;
}
程序运行后:
栈区:存放函数的参数值、局部变量。由编译器自动分配和释放。
注意事项:不要返回局部变量地址
形参数据也会放在栈区
int* funcation()
{
int a = 10;//存放在栈区,栈区的数据在函数执行完后自动释放
return &a;
}
int main()
{
int* p = funcation();
cout <<"局部变量a的值为:"<< *p << endl; //第一次正常打印,因为编译器做了保留
cout << "局部变量a的值为:" << *p << endl;
return 0;
}
堆区:由程序员分配(new)和释放(delete),若程序员不释放,程序结束时由操作系统回收。
int* funcation()
{
//new关键字,可以将数据开辟到堆区上
//指针本质上也是局部变量,存放在栈区上,但是保存的数据在堆区
int* p = new int(10); //返回的是地址,不是数据
return p;
}
int main()
{
int* p = funcation();
cout << *p << endl;
cout << *p << endl;
return 0;
}
在c++中主要利用new在堆区开辟内存
3.2 new操作符
//new的基本用法
int* p = new int(10);//在堆区创建整型变量,返回该变量的地址
delete p;//释放
int* parr = new int[10];//在堆区创建一个元素为10的整型数组,返回数组首元素的地址
delete[] arr;//释放一个数组
在堆区利用new开辟数组
int * arr= new int[10];
for(int i=0;i<10;i++)
{
arr[i]=i+100;
}
释放堆区数组
delete[] arr;
四、引用
4.1 引用的基本使用
引用:给变量起个别名。
语法:数据类型
&
别名
=原名
int a=10;
int &b=a;
4.2 引用的注意事项
1.引用必须要初始化
2.引用一旦初始化后就不可以更改
int a = 10;
int& b = a;
int c = 20;
b = c;
cout << a << endl;
cout << b << endl;
cout << c << endl;
//int& b = c; //错了!!!
4.3 引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参。
优点:可以简化指针修改实参
//地址传递
void swap01(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
//引用传递
void swap02(int& a, int& b)
{
int temp = a;
a = b;
b = temp;
}
4.4 引用做函数的返回值
注意:不要返回局部变量的引用,函数的调用可以作为左值。(局部变量存放在栈区,函数执行完后自动释放局部变量,可以加上static是数据存放在全局区,就可以返回了)
//eg.
//①不要返回局部变量的引用
int& test1()
{
int a = 10;
return a;
}
int main()
{
int& ret = test1();
cout << "ret=" << ret << endl;
cout << "ret=" << ret << endl;
test1() = 20; //②如果函数的返回值为引用,函数的调用可以作为左值
cout << "ret=" << ret << endl;
cout << "ret=" << ret << endl;
return 0;
}
4.5 引用的本质
本质:引用的本质在c++内部实现是一个指针常量(指针的指向不可以修改,指针指向的值可以修改),即 引用的本质就是一个指针常量,引用一旦初始化后就不可发生改变
int& ref = a;
//自动转换为 int* const ref = &a;指针常量是指针指向不可改,也说明为什么引用不可改
4.6 常量引用
常量引用主要用来修饰形参,防止误操作。(防止修改了形参引起了实参的改变,加上const之后,修改操作会报错,不能修改)
int &ret=10; //错了!,引用本身需要一个合法的内存空间。
/*
相当于编译器先创建一个临时变量:int temp=10;
然后进行起别名:int& ret=temp;
*/
const int& ret = 10;
注意:用常量引用之后不可以更改数据。
//eg.
int main()
{
const int& ret = 10;
ret = 100;//err
cout << "ret=" << ret << endl;
return 0;
}
五、函数高级
5.1 函数默认参数
语法:返回类型 函数名(参数=默认值){}
注意事项:
如果某个位置已经有了默认参数,那么从这个位置往后,从左到右都必须有默认值。
如果函数的声明有默认参数,函数实现就不能有默认参数。(编译器不知道用哪个参数)
5.2 函数占位参数
语法:返回类型 函数名(数据类型){} //(括号里只写数据类型,传递的参数类型必须和所写的一样)
void test1(int a, int)
{
cout << "func" << endl;
}
void test2(int a, int =10)//占位参数可以有默认参数
{
cout << "func" << endl;
}
5.3 函数重载
作用:函数名可以相同,提高复用性
满足条件:
①同一个作用域。(ex:都在全局作用域下)
②函数名称相同。
③参数类型不同/个数不同/顺序不同。
//在全局作用域
void test()
{
cout << "调用test( )" << endl;
}
void test(int a)
{
cout << "调用test(int a)" << endl;
}
void test(double a)
{
cout << "调用test(double a)" << endl;
}
void test(int a, int b)
{
cout << "调用test(int a, int b)" << endl;
}
void test(int a, double b)
{
cout << "调用test(int a, double b)" << endl;
}
void test(double a, int b)
{
cout << "调用test(double a, int b)" << endl;
}
int main()
{
test();
test(10);
test(3.14);
test(10,3.14);
test(3.14, 10);
return 0;
}
注意:函数的返回值不能作为函数重载的条件(返回值类型不同不行,有二义性)
引用作为函数重载的条件
//引用作为重载条件
void test(int &a)
{
cout << "调用test(int &a)" << endl;
}
void test(const int& b)
{
cout << "调用test(const int& b)" << endl;
}
int main()
{
int a = 10;
const int b = 10;
test(a);
test(b);
test(10);
return 0;
}
函数重载碰到默认参数
//函数重载碰到默认参数
void test(int a,int b=10)
{
cout << "调用test(int &a)" << endl;
}
void test(int a)
{
cout << "调用test(const int& b)" << endl;
}
int main()
{
test(10);//err,出现二义
return 0;
}
六、类和对象
c++面向对象三大特性:封装、继承、多态。
6.1 封装
6.1.1 封装的意义
1. 将属性和行为作为一个整体。(放在一个class里面)
2. 将属性和行为加以权限控制。
public公共权限:类内外都可以访问
protected保护权限: 类外不可以访问