前言
基础
1. 数组
1. 一维数组定义
2. 二维数组定义
2. 函数
函数的定义一般主要有5个步骤:
- 返回值类型
- 函数名
- 参数表列
- 函数体语句
- return表达式
1 2 3 4
| int add(int num1,int num2){ int sum=num1+num2; return sum; }
|
值传递时形参改变不了实参
2.1 函数的声明
作用:告诉编译器函数名称及如何调用函数,函数的实际主体可以单独定义。
2.2 函数的分文件编写
作用:让代码结构更加清晰
函数分文件编写一般有4个步骤:
- 创建后缀为.h的头文件
- 创建后缀名为.cpp的源文件
- 在头文件中写函数的声明
- 在源文件中写函数的定义
注意include后面加绝对路径
3. 指针
3.1 指针基本作用
指针的作用:可以通过指针间接访问内存
- 内存编号是从0开始记录的,一般用十六进制数字表示
- 可以利用指针变量保存地址
3.2 指针的定义和使用
定义指针:数据类型*指针变量名:
1 2 3 4 5 6 7 8 9
| int a=10; int *p; p=&a; *p=1000; cout << "a=" << a << endl; cout << "*p=" << *p << endl;
|
3.3 const修饰指针
const修饰指针有三种情况:
const修饰指针—常量指针(指针的指向可以改,指针指向的值不能改)
const修饰常量—指针常量(指针的指向不可以改,指针指向的值可以改)
const既修饰指针,又修饰常量(指针的指向和指针指向的值都不可以改)
3.4 指针和数组
利用指针访问数组中元素
1 2 3 4 5 6
| int arr[10]={1,2,3,4,5,6,7,8,9,10}; int *p=arr; cout << "利用指针来访问第一个元素" << *p << endl; p++; cout << "利用指针来访问第二个元素" << *p << endl;
|
3.5 指针和函数
作用:利用指针作函数参数,可以修改实参的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void swap(int *p1,int *p2){ int temp=*p1; *p1=*p2; *p2=temp; } int main(){ int a=10; int b=20; swap(&a,&b); cout << "a=" << a << endl; cout << "b=" << b << endl; system("pause"); return 0; }
|
4. 结构体
4.1 基本概念
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
4.2 结构体定义和使用
语法:struct 结构体名{结构体成员列表};
通过结构体创建变量的方式有三种:
- struct 结构体名 变量名
- struct 结构体吗 变量名={成员1值,成员2值…}
- 定义结构体时顺便创建变量
1 2 3 4 5 6 7 8
| struct Student{ string name; int age; int score; }s3;
struct Student s1={"张三",21,100}; Student s2={"李四",21,100};
|
4.3 结构体指针
作用:通过指针访问结构体中的成员
1 2 3 4 5 6 7 8
| struct Student{ string name; int age; int score; }; struct Student s={"张三",18,100}; struct Student *p=&s; cout << p->name << p->age << p->score;
|
核心
1. 内存分区模型
C++程序在执行时,将内存大方向划分为4个区域。
- 代码区:存放函数的二进制代码,由操作系统进行管理
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束由操作系统回收
内存四区的意义:不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程
1.1 程序运行前
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域
代码区:
存放CPU执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需 要在内存中有一份代码即可
代码区是只读的,可防止程序意外修改了它的指令
全局区:
全局变量和静态变量(static)存放在此
全局区还包含了常量区、字符串常量和其他常量也存放在此
该区域的数据在程序结束后由操作系统释放
1.2 程序运行后
栈区:
由编译器自动分配释放,存放函数的参数值、局部变量等
注意事项:不要返回局部变量的地址
堆区:
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
在C++中主要利用new在堆区开辟内存
1.3 new操作符
C++利用new操作符在堆区开辟数据
堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete
语法:new 数据类型
利用new创建的数据,回返回该数据对应的类型的指针
2. 引用
2.1 引用的基本使用
作用:给变量起别名
语法:数据类型 &别名=原名
1 2 3
| int a=10; int &b=a; cout << b <<endl;
|
引用必须初始化.引用在初始化后,就不可以发生改变
2.2 引用做函数参数
作用:函数传参时,可以利用引用的技术让形参修饰实参
优点:可以简化指针修改实参
1 2 3 4 5 6 7 8
| void myswap(int &a, int &b){ int temp = a; a = b; b = temp; }
myswap(a, b)
|
2.3 引用的本质
本质:引用的本质在C++内部实现是一个指针常量
3. 类和对象
3.1 封装
C++面向对象的三大特性为:封装、继承和多态
万事万物皆为对象,对象上有其属性和行为
封装的意义:
- 将属性和行为作为一个整体,表现生活中的事物
- 将属性和行为加以权限控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Circle{ public: int m_r; double cal(){ return 2*PI*m_r; } };
Circle c1; c1.m_r=10; cout << c1.cal() << endl;
|
类中的属性和行为统一称为成员
访问权限有三种:
- public 公共权限
- protected 保护权限
- private 私有权限
struct和class的区别
struct和class的唯一区别在于默认的访问权限不同
- struct默认权限为公共
- class默认权限为私有
成员属性设置为私有:
优点
- 将所有成员属性设置为私有,可以自己控制读写权限
- 对于写权限,我们可以检测数据的有效性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| class Person { public: void setName(string name){ m_Name=name; } string getName(){ return m_Name; } int getAge(){ m_Age=21; return m_Age; } void setLover(string lover){ m_Lover=lover; } private: string m_Name; int m_Age; string m_Lover; };
Person p; p.setName("张三"); cout << p.getName() <<endl;
|
3.2 对象的初始化和清理
如果我们不提供构造函数和析构函数,编译器会提供,编译器提供的构造函数和析构函数是空实现。
- 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作
3.2.1 构造函数的分类和调用
两种分类方式:
按参数分为:有参构造和无参构造
按类型分为:普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| class Person{ public: Person(){ cout << "Person无参数构造函数被调用" << endl; } Person(int a){ age=a; cout << "Person有参数构造函数被调用" << endl; } Person(const Person &p){ age=p.age; cout << "Person拷贝构造函数被调用" << endl; } int age; }; void test01(){ Person p1; Person p2(10); Person p3(p2); Person p4; Person p5=Person(10); Person p6=Person(p5); Person p7=10; Person p8=p7; }
|
3.3 C++对象模型和this指针
在C++中,类内的成员变量和成员函数分开存储
只有非静态成员变量才属于类的对象上
每个非静态成员函数只会诞生一份函数实例。也就是说多个同类型的对象会共用同一块代码
this指针指向被调用的成员函数所属的对象
this指针是隐含每一个非静态成员函数内的一种指针
this指针的用途:
- 当形参和成员变量同名时,可以用this指针来区分
- 在类的非静态成员函数中返回对象本身,可使用return *this
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public: Person(int age){ this->age=age; } Person& PersonAddAge(Person &p){ this->age+=p.age; return *this; } int age; }; void test01(){ Person p(18); cout << "年龄为" << p.age <<endl; } void test02(){ Person p1(10); Person p2(10); p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1); cout << "p2年龄为" << p2.age <<endl; }
|
3.4 友元
在程序里有些私有属性也想让类外特殊的一些函数或者类进行访问,就需要用到友元的技术
友元的目的是让一个函数或者类访问另一个类中私有成员
友元的关键字为friend
友元的三种实现:
全局函数做友元
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Building{ friend void goodGay(Building *builing); public: Building(){ m_sittingRoom="客厅"; m_bedroom="卧室"; } public: string m_sittingRoom; private: string m_bedroom; };
void goodGay(Building *builing){ cout << "好基友的全局函数正在访问:" << builing->m_sittingRoom <<endl; cout << "好基友的全局函数正在访问:" << builing->m_bedroom <<endl;
} void test01(){ Building building; goodGay(&building); }
|
类做友元
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| class Building{ friend class GoodGay; public: Building(); public: string m_sittingroom; private: string m_bedroom; }; class GoodGay{ public: GoodGay(); public: Building *building; void visit(); };
Building::Building(){ m_sittingroom="客厅"; m_bedroom="卧室"; } GoodGay::GoodGay(){ building=new Building; } void GoodGay::visit(){ cout << "好基友类正在访问:"<< building->m_sittingroom <<endl; cout << "好基友类正在访问:"<< building->m_bedroom <<endl; } void test01(){ GoodGay gg; gg.visit(); }
|
成员函数做友元
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| class Building; class GoodGay{ public: GoodGay(); void visit(); void visit2(); Building *building; }; class Building{ friend void GoodGay::visit(); public: Building(); public: string m_sittingroom; private: string m_bedroom; };
Building::Building(){ m_sittingroom="客厅"; m_bedroom="卧室"; } GoodGay::GoodGay(){ building=new Building; } void GoodGay::visit(){ cout << "visit函数正在访问" << building->m_sittingroom <<endl; cout << "visit函数正在访问" << building->m_bedroom<<endl;
}; void GoodGay::visit2(){ cout << "visit2函数正在访问" << building->m_sittingroom <<endl; }; void test01(){ GoodGay gg; gg.visit(); gg.visit2(); }
|
3.5 继承
继承是面向对象三大特征之一。
3.5.1 继承的基本语法
// 语法: class 子类 : 继承方式 父类
// 子类也称为派生类
// 父类也称为基类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| class BasePage{ public: void header(){ cout << "公共头部" << endl; } void footer(){ cout << "公共底部" << endl; } }; class Java:public BasePage{ public: void cont(){ cout << "Java" <<endl; } }; class Python:public BasePage{ public: void cont(){ cout << "Python" <<endl; } }; void test01(){ Java java; java.cont(); Python py; py.cont(); }
|
3.5.2 继承方式
继承方式有三种:
3.5.3 继承中的对象模型
父类中所有非静态成员属性都会被子类继承下去
父类中私有成员属性是被编译器给隐藏了,因此是访问不到的,但是确实被继承了
3.5.4 继承中的构造和析构顺序
子类继承父类后,当创建子类对象,也会调用父类的构造函数
继承中的构造和析构的顺序如下:先构造父类再构造子类,析构顺序与构造顺序相反
3.5.5 继承同名成员处理方式
- 访问子类同名成员,直接访问即可
- 访问父类同名成员,需要加作用域
当子类与父类拥有同名的成员函数,子类会隐藏父类中所有同名成员函数,加作用域即可访问
4. 多态
4.1 多态的基本概念
多态分为两类:
- 静态多态:函数重载和运算符重载属于静态多态,复用函数名
- 动态多态:派生类和虚函数实现运行时多态
静态多态和动态多态的区别:
- 静态多态的函数地址早绑定 - 编译阶段确定函数地址
- 动态多态的函数地址晚绑定 - 运行阶段确定函数地址