C++面向对象学习笔记(一)
1. 内存分区模型
C++在执行时,将内存大方向划分为4个区域
-
代码区:函数体的二进制代码,由操作系统进行管理
-
全局区:存放全局变量和,静态变量以及常量
-
栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
-
堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
意义
不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程。
1.1 程序运行前
程序编译后,生成exe可执行程序,执行前分为两个趋于
代码区
存放cpu执行的机器指令(就是你写的代码)
代码区是共享的,频繁执行的程序,只需要有一份代码即可
代码区是只读的,防止程序意外修改
全局区
存放全局变量和静态变量
全局区还包含了常量区,存放字符串常量和其他常量
该区域的内存在程序结束后由操作系统释放
#include <iostream>
using namespace std;
//全局变量
int g_a = 20;
int g_b = 20;
//const修饰的全局变量,全局常量
const int c_g_a = 10;
int main()
{
//局部变量,写在函数体内的
int a = 10;
int b = 20;
cout << "a:" << (int)&a << endl;
cout << "b:" << (int)&b << endl;
cout << "g_a:" << (int)&g_a << endl;
cout << "g_b:" << (int)&g_b << endl;
//静态变量
static int s_a = 10;
cout << "s_a:" << (int)&s_a << endl;
//常量
//字符串常量
cout << "hello world:" << (int)&"hello world" << endl;
//const修饰的变量
//const修饰的全局变量,const修饰的局部变量
const int c_l_a = 10;
cout << "c_l_a:" << (int)&c_l_a << endl;
cout << "c_g_a:" << (int)&c_g_a << endl;
system("pause");
return 0;
}
```
输出结果:
```c++
a:2130114772
b:2130114804
g_a:2061226064
g_b:2061226068
s_a:2061226072
hello world:2061216720
c_l_a:2130114836
c_g_a:2061216864
1.2 程序运行后
栈区
由编译器自动分配释放,存放函数的参数值,局部变量等
注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放(但是我用vs2022可以一直保留局部变量)
堆区
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
在c++中主要利用new在堆区开辟内存
由程序员手动开辟,手动释放,释放利用操作符delete
语法:new 数据类型
利用new创建的数据,会返回该数据对应的类型的指针
2. 引用
2.1 引用的基本使用
作用:给变量起别名
语法:数据类型 &别名=原名
2.2 引用的注意事项
- 引用必须初始化
- 引用在初始化后,不可以改变

2.3 引用做函数参数
作用:函数传参时,可以利用引用的技术让实参修饰形参
优点:可以简化指针修改实参
void mySwap01(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
总结通过引用参数产生的效果和地址传递是一样的,引用的语法更清楚简单
2.4 引用做参数的返回值
作用:引用是可以作为函数的返回值存在的
注意不要返回局部变量引用
用法:函数调用作为左值(等号左边)
int& ref = test01();
test01() = 1000;
2.5 引用的本质
本质:引用的本质在C+=内部实现是一个指针常量
int& ref = a;//自动转换为 int* const ref = &a;
ref = 20;//自动转换为 * ref = 20;
结论C++推荐使用引用技术,因为语法方便,引用本质是指针常量,但是所有的指针编译器都帮我们做了
2.6 常量引用
作用:常量引用主要用来修饰形参,防止误操作
在函数形参列表中,可以加const修饰形参,防止形参改变实参
void print(const int &a)
3.函数提高
3.1 函数的默认参数
形参可以有默认值
语法:返回类型值 函数名 (参数=默认值){}
注意:
- 如果自己传入数据,则用自己传的,没有传则用默认的
- 默认参数必须在形参列表结尾
- 函数声明和实现只能有一个有默认参数
3.2 函数占位参数
函数的形参列表里可以有占位参数,用来占位,调用函数时必须填补该位置
语法:返回值类型 函数名 (数据类型){}
占位参数可以有默认参数
3.3 函数重载
3.3.1 函数重载概述
作用:函数名可以相同,提高复用性
函数重载满足条件:
- 同一个作用域下
- 函数名相同
- 函数参数类型不同或个数不同或顺序不同
void func()
{
cout << "func的调用" << endl;
}
void func(int a)
{
cout << "func的调用" << endl;
}
void func(double a)
{
cout << "func的调用" << endl;
}
void func(int a,double b)
{
cout << "func的调用" << endl;
}
void func(double a,int b)
{
cout << "func的调用" << endl;
}
注意:
-
函数的返回值不可以作为函数重载的条件
-
引用可以作为重载的条件
注意:
int &a和const int &a可以作为重载的条件。当传入变量(int a=10)时调用使用int &a的函数,当直接传入确定数值(10)时调用使用const int &a的变量。 -
函数碰到默认参数时,要排除默认参数后剩下的函数参数类型不同或个数不同或顺序不同,才可以重载。
4 类和对象
(我感觉和结构体很像,但是百度之后发现,他们默认的访问权限不一样,而且类是有继承的,结构体没有继承。)
- C++结构体内部成员变量及成员函数默认的访问级别是public,而c++类的内部成员变量及成员函数的默认访问级别是private。
- C++结构体的继承默认是public,而c++类的继承默认是private。
C++面向对象的三大特性:封装,继承,多态
C++认为万事万物皆有对象,对象上有其属性的行为
4.1 封装
4.1.1封装的意义
封装是C++面向对象三大特性之一
封装的意义:
-
将属性和行为作为一个整体,表现生活中的事物
-
将属性和行为加以权限控制
意义一:
在设计的时候,属性和行为写在一起,表现事物
语法:class 类名{ 访问权限: 属性/行为 }
#include<iostream>
using namespace std;
//设计一个圆类,计算圆周长
//圆周率
const double Π = 3.14;
//class代表一个类,类后面紧跟着的就是类名称
class circle
{
//访问权限
//公共权限
public:
//属性
//半径
int m_r;
//行为
//获取圆的周长
double calcuateZC()
{
return 2 * Π * m_r;
}
};
int main()
{
//通过圆类,创建具体的圆(对象)
circle c1;
//给圆对象的属性赋值
c1.m_r = 10;
cout << "圆的周长为:" << c1.calcuateZC() << endl;
system("pause");
return 0;
}
类中的属性和行为统一称为成员
- 属性:成员属性,成员变量
- 行为:成员函数,成员方法
也可以通过行为给属性赋值
意义二:
类在设计时可以把属性和行为放在不同的权限下,加以控制
访问权限有三种:
- public 公共权限 类内类外都可以访问呢
- protected 保护权限 类内可以访问,类外不可以
- private 私有权限 类内可以访问,类外不可以
4.1.2成员属性设置为私有
优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
4.2 对象的初始化和清理
C++中每个对象也都会有初始设置以及对象销毁前的清理数据的设置
4.2.1 构造函数和析构函数
对象的初始化和清理也是两个非常重要的安全问题
一个对象或者变量没有初始状态,对其使用后果是未知
同样的使用完一个对象或比那辆,没有及时清理,也会造成一定的安全问题
C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。
对象的初始化和清理工作是编译器强制要我们做的事情,因此我们不提供构造和析构,编译器会提供
编译器提供的构造和析构函数是空实现
- 构造函数:主要作用与创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
- 析构函数:主要作用与对象销毁前系统自动调用,执行一些清理工作
构造函数语法:类名()}{}
- 构造函数没有返回值也不写void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次
析构函数语法~类名(){}
- 析构函数,没有返回值也不写void
- 函数名称与类名相同,在名称前加上符合~
- 析构函数不可以有参数,因此不可以发生重载
- 程序在对象销毁前自动调用析构,无须手动调用,而且只会调用一次
class Person
{
public:
Person()
{
cout << "Person构造函数调用" << endl;
}
~Person()
{
cout << "Perosn 析构函数调用" << endl;
}
};
4.2.2 构造函数的分类及调用
两种分类方式:
- 按参数分:有参构造和无参构造
- 按类型分:普通构造和拷贝构造
三种调用方式:
- 括号法
Person p//无参构造函数
注意调用无参函数不要用()
Person p(10)//有参构造函数
Person p(p1)//拷贝构造函数
- 显示法
Person p1//无参函数
Person p2 = Person (10)//有参构造函数
Person p3 = Person (p2)//拷贝构造函数
Perosn (10);匿名对象 特点:当执行结束后,系统会立即回收掉匿名对象
注意:不要利用拷贝构造函数初始化匿名对象
- 隐式转换法
Person p4 = 10
Person p5 = p4//拷贝构造
4.2.3 拷贝构造函数调用时机
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数传值
- 以值方式返回局部对象
4.2.4 构造函数调用规则
默认情况下,C++编译器至少会给一个类添加3个函数
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
- 如果用户定义有参构造函数,C++不再提供默认无参构造,但是会提供默认拷贝构造
- 如果用户定义拷贝构造函数,C++不会再提供其他构造函数
4.2.5 深拷贝与浅拷贝
(深拷贝是面试经典问题,也是常见的一个坑)
浅拷贝:简单的赋值拷贝操作
带来的问题:堆区的内存重复释放
深拷贝:在堆区重新申请空间,进行拷贝操作
总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
4.2.6 初始化列表
作用:
C++提供了初始化列表语法,用来初始化属性
语法:构造函数():属性1(值1),属性2(值2)...{}
class person
{
public:
/*传统初始化操作
person(int a, int b, int c)
{
m_a = a;
m_b = b;
m_c = c;
}*/
//初始化列表初始化属性值
person() :m_a(10), m_b(20), m_c(30)
//person(int a,int b, int c,):m_a(a),m_b(b),m_c(c)
{
}
int m_a;
int m_b;
int m_c;
};
void test01()
{
person p;
//person p(10, 20, 30);
cout << "m_a=" << p.m_a << " m_b=" << p.m_b << " m_c=" << p.m_c << endl;
}
4.2.7 类对象作为类成员
C++类中的成员可以是另一个类中的对象,我们称该成员为对象成员
如:
class A {};
class B
{
A a;
};
注意:
当其他类对象作为本类成员,构造时候先构造类对象,再构造自身
析构时顺序与构造相反
4.2.8 静态成员
静态成员就是在成员变量和成员函数前加上关键字static,成为静态成员
静态成员分为:
-
静态成员变量
-
所有对象共享同一份数据
-
在编译阶段分配内存
-
类内声明
-
-
静态成员函数
-
所有对象共享同一个函数
-
静态成员函数只能访问静态成员变量
-
(1)静态成员变量
class person
{
public:
//类内声明
static int m_a;
};
//类外初始化
int person::m_a = 100;
void test01()
{
person p;
cout << p.m_a << endl;
person p1;
p.m_a = 200;
cout << p.m_a << endl;//输出值为200(所有对象共享同一份数据)
}
静态成员变量不属于某个对象上,所有对象都共享同一份数据
因此静态成员变量有两种访问方式
-
通过对象进行访问
-
通过类名进行访问
cout << p.m_a << endl;
cout << person::m_a << endl;
注意:静态成员变量也是有访问权限的。
(2)静态成员函数
void test01()
{
//通过对象访问
person p;
p.func();
//通过类名访问
person::func();
}
int main()
{
test01();
system("pause");
return 0;
}
注意:静态成员函数也是有访问权限的

浙公网安备 33010602011771号