
3内存模型
C++在执行期间将内存分为四个区域
代码区:存放函数体的二进制代码,由操作系统进行管理
全局区:存放全局变量静态变量以及常量
栈区:由编译器自动分配释放,存放函数的参数值、局部变量等
堆区:由程序员分配和释放,若程序员不释放,程序结束后由操作系统回收
分区的意义:给不同的区域存放的数据赋予不同的生命周期,给我们更大的灵活编程
3.1代码区
程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域:代码区和全局区
代码区:存放的是CPU执行的机器指令,共享的,对于频繁执行的程序只需要在内存中保存一份即可。只读的防止程序意外的修改了它的指令。
3.2全局区
存放了全局变量、静态变量、常量(const修饰的全局常量和字符串常量)。该区域的数据在程序结束后由操作系统释放。

3.3栈区
由编译器自动分配和释放,存放函数的参数值、局部变量等。
注意:不要返回局部变量的地址,因为栈区的数据在函数执行完之后自动释放

可以看到在方法中返回地址在第一次会打印出来,第二次就不会打印了。这是因为在第一次的时候编译器做了保留。
这里我又执行了一次一样的代码输出的两次都是地址:

很迷茫,最后问了GPT ,他的回答:


3.4堆区
由程序员分配和释放,程序员不释放程序结束时由操作系统回收
在C++中主要利用new在堆区开辟内存。
指针的本质也是局部变量,也是放在栈上,指针保存的数据是放在了堆区

Delete释放堆内存的数据,如果释放后再访问就是非法操作 会报错

4面向对象
4.1函数的高级应用
4.1.1函数的默认参数
如果我们自己传入数据,就用传入的数据,如果用户不传值,就用默认参数。

注意事项:
1 如果一个位置开始有了默认参数,那么从这个位置往后从左到右都必须有默认值

2 函数的声明和实现只能有一个有默认参数。

4.1.2函数的占位参数
C++的函数形参列表中可以有一个占位参数,用来做占位,调用函数时必须填补该位置。
使用:返回值类型 函数名字(数据类型)
占位符也可以有默认值

4.1.3函数重载
函数名字可以相同,提高复用性。
函数重载的条件:
1 同一个作用域下
2 函数名相同
3 函数的形参类型、个数、顺序不同
注意:函数的返回值不可以作为函数重载的条件

4.1.4函数重载的注意事项
引用作为重载条件


函数重载避免出现默认参数

4.2封装
4.2.1封装的意义
将属性和行为封装成一个整体,表现生活中的事物,将属性和行为加以权限控制
4.2.2访问权限
public:公共权限,成员类内和外都能访问
protected:保护权限,类内能访问 类外不可以访问,子类可以访问父类的保护成员
private:私有权限,类内能访问 类外不可以访问,子类不可以访问父类的保护成员
私有成员和保护成员在类外是访问不到的,可以用get、set方法进行访问。

4.2.3struct和class
二者都可以修饰一个类。
struct默认的访问权限是公共,class默认的权限是私有的
4.3对象的初始化和清理
4.3.1构造和析构
构造函数:主要在创建对象时给对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用。
1 函数名与类名同名 函数名()
2无返回值也不写void
3构造函数可以有参数,因此可以发生重载
4程序在初始化的时候会自动调用构造,无需手动调用,而且只会调用一次
析构函数:在于对象销毁前系统自动调用,执行一些清理工作。
1 函数名与类名同名 ~函数名()
2无返回值也不写void
3构造函数可以有参数,因此可以发生重载
4 程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
4.3.2 构造函数的分类以及调用
分类方式:
有参构造和无参构造
普通构造和拷贝构造
调用方式:
1 括号法
注意:调用默认构造函数时不要加()

2 显示法

3 隐式转换法

4.3.3拷贝构造函数的调用时机
1使用一个已经创建完的对象来初始化一个新对象
2 值传递的方式给函数参数传值
3 以值方式返回局部对象,可以看到第一次调用的地址和第二次的不同,不是同一个对象。

4.3.4构造函数的调用规则
默认情况,C++会给一个类至少添加三个函数:
1 默认构造函数
2 默认析构函数
3 默认拷贝构造函数
构造函数的调用规则如下:
如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造。
如果用户自定义拷贝构造,C++不会再提供其他构造函数。
4.3.5深拷贝和浅拷贝
浅拷贝:简单的赋值操作
深拷贝:在堆区重新申请空间,进行拷贝操作
如果利用编译器提供的拷贝构造函数,会做浅拷贝操作。
下面是一段代码,创建一个对象放在了堆内存里,然后在析构函数里释放。

这样写代码会出现问题:

根据先进后出的原则,会先释放s1,再释放s,但是s和s1会指向同一个堆内存,会导致同一块区域的堆内存释放两次,堆内存重复释放就会报错。
解决浅拷贝的问题就是深拷贝。
深拷贝的实现:创建一块新的内存地址就不会报错了。

如果属性有在堆区开辟的,一定要自己提供一个深拷贝函数,防止浅拷贝带来的问题。

4.3.6初始化列表
构造函数用来给属性进行初始化的操作,初始化列表也能用来给属性进行初始化。
语法:构造函数():属性1(值1),属性2(值2){ }

进行值的传入:

4.3.7 类对象作为类成员
class A{ };
class B {
A a;
};
C++中一个类的成员可以是另一个类的对象,这中成员被称为对象成员。在上面的例子中,B中有成员A ,A就是对象成员。
此时创建一个B对象,会先初始化A的构造:

总结:当其他类对象作为本类成员时,构造的时候先构造类对象,再构造自身,析构的顺序相反。
4.3.8静态成员
就是在成员函数或者变量前面加上static
静态成员变量:
所有对象共享同一份数据
在编译阶段分配内存,在全局区
类内声明,类外初始化

静态成员变量有两种访问方式:
1 通过对象访问
2 通过类名访问

静态成员函数:
所有对象共享一个函数
静态成员函数只能访问静态成员变量