1 内存四区
C++程序在执行时,将内存大方向划分为4个区域
- 代码区:存放函数体的二进制代码,由操作系统进行管理的
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
1.1 代码区
存放程序执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
1.2 全局区
存放全局变量,全局/局部静态变量,全局常量,字面值常量(就是写在代码里面的常量如字符串)
全局区和代码区的生命周期贯穿整个程序的运行期间
1.3 栈区
存放函数的参数值,局部变量/常量等,由编译器自动分配释放
1.4 堆区
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
1.5 new 与 delete
利用new 数据类型在堆区开辟数据,使用delete 指针释放内存
利用new创建数据时,返回对应的指针
示例:
1 2 3 4 5 6 7 8 9
| int main() {
int *p = new int(10); cout << *p << endl; delete p; int* arr = new int[10]; delete[] arr; }
|
2 引用
2.1 引用的基本使用
语法: 数据类型& 别名 = 原名
引用的本质是一个指针常量
示例:
1 2 3 4 5 6 7 8 9 10 11
| int main() {
int a = 10; int &b = a;
b = 100;
cout << "a = " << a << endl;
return 0; }
|
2.2 引用与函数
引用传参与指针传参效果相同
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| void mySwap(int& a, int& b) { int temp = a; a = b; b = temp; }
int main() {
int a = 10; int b = 20;
mySwap0(a, b); cout << "a:" << a << " b:" << b << endl;
return 0; }
|
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| int& test() { static int a = 20; return a; }
int main() {
int& ref = test(); cout << "ref = " << ref << endl; test() = 1000; cout << "ref = " << ref << endl; return 0; }
|
2.3 const修饰引用
**作用:**常量引用主要用来修饰形参,防止误操作
- 非常量引用只能绑定到可修改的左值(lvalue),例如变量。
- 常量引用可以绑定到常量、临时对象(如字面值)以及可修改的左值。
示例:
1 2 3 4 5 6 7 8 9 10 11
| void showValue(const int& v) { cout << v << endl; }
int main() {
int a = 10; showValue(a); return 0; }
|
3 类和对象
C++面向对象的三大特性为:封装、继承、多态
3.1 封装
3.1.1 类
将属性和行为作为一个整体,将具有相同性质的对象,我们可以抽象称为类
语法: class 类名{ 访问权限: 属性 / 行为 };
3.1.1 访问权限
类在设计时,可以把属性和行为放在不同的权限下,加以控制
- public 公共权限,类内可以访问, 类外可以访问,继承的可以访问
- protected 保护权限,类内可以访问 ,类外不可以访问,继承的可以访问
- private 私有权限,类内可以访问 ,类外不可以访问,继承的也不可以访问
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 Person { public: string m_Name;
protected: string m_Car;
private: int m_Password;
public: void func() { m_Name = "张三"; m_Car = "拖拉机"; m_Password = 123456; } };
int main() {
Person p; p.m_Name = "李四";
return 0; }
|
3.1.2 struct 和 class 区别
在C++中struct和class区别就在于默认的访问权限不同
struc默认权限为公共
class默认权限为私有
3.1.3 属性设置为私有
将所有属性设置为私有,通过行为来读写,可以自己控制读写权限,检测数据的有效性
可以在方法后加const强制只读,不修改成员属性
示例:
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
| class Person { public:
void setName(string name) { m_Name = name; } string getName() const { return m_Name; }
int getAge() { return m_Age; } void setAge(int age) { if (age < 0 || age > 150) { cout << "你个老妖精!" << endl; return; } m_Age = age; }
void setLover(string lover) { m_Lover = lover; }
private: string m_Name; int m_Age; string m_Lover; };
|
3.1.4 类分文件编写
头文件写方法声明和属性,cpp写定义,在需要调用的地方包含对应的头文件即可
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #pragma once #include <iostream> using namespace std;
class Person { public: string get_name() const; int get_age() const; void set_name(string name); void set_age(int age); void set_lover(string lover);
private: string m_name; int m_age = 0; string m_lover; };
|
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
| #include "person.h"
void Person::set_name(string name){ m_name = name; } string Person::get_name() const{ return m_name; } int Person::get_age() const{ return m_age; } void Person::set_age(int age) { if (age < 0 || age > 150) { cout << "你个老妖精!" << endl; return; } m_age = age; } void Person::set_lover(string lover) { m_lover = lover; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include "person.h"
int main() {
Person p; p.set_name("张三"); cout << "姓名: " << p.get_name() << endl;
p.set_age(50); cout << "年龄: " << p.get_age() << endl;
p.set_lover("苍井");
return 0; }
|
3.2 对象的初始化和清理
3.2.1 构造函数和析构函数
构造函数和析构函数将会被编译器自动调用,完成对象初始化和清理工作,这是编译器强制要我们做的事情,因此如果我们不提供构造和析构函数,编译器会提供空实现的函数
构造函数语法:类名(){}
- 没有返回值, 函数名与类名相同
- 构造函数可以有参数,因此可以发生重载
- 自动调用且只会调用一次
析构函数语法: ~类名(){}
- 没有返回值, 函数名与类名相同
- 不可以有参数,不可以发生重载
- 自动调用且只会调用一次
3.2.2 构造函数的分类及调用
按参数分为: 有参构造和无参构造
按类型分为: 普通构造和拷贝构造
示例:
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 39 40 41 42
| class Person { public: Person() { cout << "无参构造函数!" << endl; } Person(int a) { age = a; cout << "有参构造函数!" << endl; } Person(const Person& p) { age = p.age; cout << "拷贝构造函数!" << endl; } ~Person() { cout << "析构函数!" << endl; } public: int age; };
int main() {
Person p; Person p1(10); Person p2(p1); Person p3 = Person(10); Person p4 = Person(p3); Person p5 = 10; Person p6 = p5; return 0; }
|
在函数值传参 / 返回对象 的时候,均会触发拷贝构造
编译器会默认提供 默认拷贝构造函数 / 默认构造函数 / 析构函数
若写了有参构造,编译器就不提供默认构造
若写了拷贝构造,编译器就不提供默认拷贝构造函数 / 默认构造函数
3.2.3 深拷贝与浅拷贝
**浅拷贝:**将属性简单赋值拷贝到新对象,如默认拷贝操作
**深拷贝:**在堆区重新申请空间,进行拷贝操作
当属性中包含指针,浅拷贝将旧对象拥有的指针简单拷贝赋值到新对象,使两个对象拥有的指针指向同一块内存,在析构函数中释放内存时,就会出现内存重复释放的错误
因此要重写拷贝函数,重新申请内存实现深拷贝
示例:
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
| class Person { public: Person(int height) { m_height = new int(height); }
Person(const Person& p) { m_height = new int(*p.m_height); }
~Person() { if (m_height != nullptr) { delete m_height; m_height = nullptr; } }
int* m_height; };
int main() { Person p1(180); Person p2(p1);
return 0; }
|
3.2.4 初始化列表
用来初始化属性
语法:构造函数():属性1(值1),属性2(值2)... {}
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Person { public:
Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) { } private: int m_A; int m_B; int m_C; };
|
3.2.5 类对象作为类成员
C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员
示例:
1 2 3 4 5
| class A {} class B { A a; }
|
3.2.6 静态成员
静态成员就是在成员前加上关键字static
静态成员变量
- 所有对象共享同一份数据,在编译阶段分配内存
- 类内声明,类外初始化
静态成员函数
- 所有对象共享同一个函数
- 静态成员函数只能访问静态成员变量(否则无法区分使用的属性是哪个对象)
**示例1 :**静态成员变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Person { public: static int m_A;
private: static int m_B; }; int Person::m_A = 10; int Person::m_B = 10;
int main() {
Person p1; cout << "p1.m_A = " << p1.m_A << endl; cout << "m_A = " << Person::m_A << endl; return 0; }
|
**示例2:**静态成员函数
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
| class Person {
public: static void func() { m_A = 100; }
static int m_A; int m_B; }; int Person::m_A = 10;
int main() {
Person p1; p1.func();
Person::func();
return 0; }
|
3.3 C++对象模型和this指针
在C++中,类内的成员变量和成员函数分开存储,只有非静态成员变量才属于类的对象上
空对象占用的内存为一个字节
3.3.1 this指针
this指针指向所属的对象
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 22 23 24 25 26 27 28 29 30 31
| class Person { public:
Person(int age) { this->age = age; }
Person& PersonAddPerson(Person p) { this->age += p.age; return *this; }
int age; };
int main() { Person p1(10); cout << "p1.age = " << p1.age << endl;
Person p2(10); p2.PersonAddPerson(p1).PersonAddPerson(p1).PersonAddPerson(p1); cout << "p2.age = " << p2.age << endl; return 0; }
|
3.3.2 空指针访问成员函数
C++中空指针也是可以调用成员函数的,但是成员函数不能用到this指针
示例:
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
| class Person { public:
void ShowClassName() { cout << "我是Person类!" << endl; }
void ShowPerson() { if (this == NULL) { return; } cout << mAge << endl; }
public: int mAge; };
int main() { Person * p = NULL; p->ShowClassName(); p->ShowPerson(); }
|
3.3.3 const修饰成员函数
常函数:
- 成员函数后加
const后我们称为这个函数为常函数
- 常函数内不可以修改成员属性
- 成员属性声明时加关键字
mutable后,在常函数中依然可以修改
常对象:
- 声明对象前加
const称该对象为常对象
- 常对象只能调用常函数
示例:
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
| class Person { public: Person() { m_A = 0; m_B = 0; }
void ShowPerson() const { this->m_B = 100; }
public: int m_A; mutable int m_B; };
int main() {
const Person person; cout << person.m_A << endl; person.m_B = 100; return 0; }
|
3.4 友元
**作用:**让类外特殊的一些函数或者类进行访问私有属性
关键字:friend
友元的三种实现
4.4.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 32 33 34 35
| class Building { friend void goodGay(Building* building);
public:
Building() { this->m_SittingRoom = "客厅"; this->m_BedRoom = "卧室"; }
public: string m_SittingRoom;
private: string m_BedRoom; };
void goodGay(Building* building) { cout << "好基友正在访问: " << building->m_SittingRoom << endl; cout << "好基友正在访问: " << building->m_BedRoom << endl; }
int main(){
Building b; goodGay(&b); return 0; }
|
3.4.2 类做友元
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 39 40 41 42 43 44 45 46 47 48 49 50 51
| class Building; class goodGay { public:
goodGay(); void visit();
private: Building *building; };
class Building { friend class goodGay;
public: Building();
public: string m_SittingRoom; private: string m_BedRoom; };
Building::Building() { this->m_SittingRoom = "客厅"; this->m_BedRoom = "卧室"; }
goodGay::goodGay() { building = new Building; }
void goodGay::visit() { cout << "好基友正在访问" << building->m_SittingRoom << endl; cout << "好基友正在访问" << building->m_BedRoom << endl; }
int main(){
goodGay gg; gg.visit(); return 0; }
|