C++基础笔记
本章主要讲了在c的基础上,c++的拓扑基础的部分,还有内存的分配。
1. 内存分区模型
C++程序在执行时,将内存大方向划分为4个区域
1. 代码区:存放函数体的二进制代码,由操作系统进行管理的
2. 全局区:存放全局变量和静态变量以及常量
3. 栈区:由编译器自动分配释放, 存放函数的参数值,局部变量等
4. 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
1.1 程序运行前
代码区:
存放 CPU 执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令
全局区:
全局变量和静态变量存放在此.
全局区还包含了常量区, 字符串常量和其他非局部常量也存放在此.
该区域的数据在程序结束后由操作系统释放.
示例:
//全局变量
int g_a = 10;
//全局常量
const int c_g_a = 10;
int main() {
//局部变量
int a = 10;
//打印地址
cout << "局部变量a地址为: " << (int)&a << endl;
cout << "全局变量g_a地址为: " << (int)&g_a << endl;
//静态变量
static int s_a = 10;
cout << "静态变量s_a地址为: " << (int)&s_a << endl;
cout << "字符串常量地址为: " << (int)&"hello world" << endl;
cout << "全局常量c_g_a地址为: " << (int)&c_g_a << endl;
const int c_l_a = 10;
cout << "局部常量c_l_a地址为: " << (int)&c_l_a << endl;
}
总结:
1.C++中在程序运行前分为全局区和代码区
2.代码区特点是共享和只读
3.全局区中存放全局变量、静态变量、常量
4.常量区中存放 const修饰的全局常量 和 字符串常量
1.2 程序运行后
栈区:
由编译器自动分配释放, 存放函数的参数值,局部变量等
注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
堆区:
由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
在C++中主要利用new在堆区开辟内存
示例:
int* func() {
int* a = new int(10);
return a;
}
int * func2() {
int a = 10;
return &a;
}
int main() {
int *p = func();
cout << *p << endl; //输出10
cout << *p << endl; //输出10(没有被释放掉)
*p = func2()
cout << *p << endl; //输出10
cout << *p << endl; //输出0(被释放掉)
}
总结:
1.堆区数据由程序员管理开辟和释放
2.堆区数据利用new关键字进行开辟内存
1.3 new操作符
C++中利用new操作符在栈区开辟空间,开辟后可以用delete进行删除
语法
new int(10);
new int[10];
示例
int main() {
int* a = new int(10); //开辟一个长度为int的内存空间,命名为a
cout << *a << endl;
delete a; //释放变量a
int* arr = new int[10]; //开辟一个长度为10*int数组内存空间,命名为arr
arr[0] = 100;
cout << arr[0] << endl;
delete[] arr; //释放数组arr
}
2. 引用
C++推荐使用引用技术,因为语法方便,有指针特性,但比指针节省不少代码。
2.1 引用的基本使用
引用相当于给变量起一个别名,可以通过引用变量访问原来的变量。
语法:
int &b = a;
示例:
int main() {
int a = 10;
int &b = a; // 引用必须初始化
//int c = 10; &b = c; // 引用初始化后不可更改指向的变量
cout << "a = " << a << endl; // 10
cout << "b = " << b << endl; // 10
b = 100; // b更改后,a也跟着改变
cout << "a = " << a << endl; // 100
cout << "b = " << b << endl; // 100
}
2.2 引用做函数参数
函数传参时,可以利用引用的技术让形参修饰示参,简化指针修改示参 。
语法:
void func(int &a, int &b){
// someCode
}
示例:
//地址传递
void mySwap02(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
//引用传递
void mySwap03(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int a = 10, b = 20;
mySwap02(&a, &b); // 交换成功
cout << "a:" << a << " b:" << b << endl;
mySwap03(a, b); //交换成功,写法比指针简单
cout << "a:" << a << " b:" << b << endl;
}
2.4 引用做函数返回值
引用是可以接收函数的静态返回值存在的,不要引用局部变量的返回值!
语法:
int& a = func();
示例:
//返回静态变量
int& test02() {
static int a = 20;
return a;
}
int main() {
int& ref = test02(); //ref引用了a;
cout << "ref = " << ref << endl; // 20
test02() = 1000;
cout << "ref = " << ref << endl; // 1000
ref = 200;
cout << "ref = " << ref << endl; // 200
}
2.5 常量引用
可以用const修饰引用来达到常量引用的目的,本质是常量指针常量。初始化常量引用可以直接赋值
语法:
const int& a = 100;
const int& a = b;
示例:
void showValue(const int& v) {
// v += 10; // 不可以更改值
cout << v << endl;
}
int main() {
// int& ref = 10; // 引用本身需要一个合法的内存空间,因此这行错误
// 加入const就可以了,编译器优化代码,int temp = 10; const int& ref = temp;
const int& ref = 10;
// ref = 100; // 加入const后不可以修改变量
cout << ref << endl;
// 函数中利用常量引用防止误操作修改示参
int a = 10;
showValue(a);
}
2.6 引用的本质
引用的本质在c++内部示现是一个指针常量.
说明:
void func(int& ref){
// 将形参int& ref转换为 int * const ref;
ref = 100; // 相当于*ref = 100
}
int main(){
int a = 10;
int& ref = a; // 相当于int* const ref = &a; 常量指针不能更改,所以引用也不能更改
ref = 20; // *ref = 20;
cout << "a:" << a << endl; // 20
cout << "ref:" << ref << endl; // 20
func(ref);
cout << "a:" << a << endl; // 100
cout << "ref:" << ref << endl; // 100
}
3. 函数补充
C++的函数对比C多了很多特性,本章主要是对C函数特性的补充。
3.1 函数的默认参数
语法:
int func(int a, int b = 10, int c = 10) {
// someCode
}
示例:
// 使用默认参数条件
// 1. 如果某个位置参数有默认值,那么从这个位置往后,从左向右,必须都要有默认值
int func(int a, int b = 10, int c = 10) {
return a + b + c;
}
// 2. 如果函数声明有默认值,函数示现的时候就不能有默认参数
int func2(int a = 10, int b = 10);
int func2(int a, int b) {
return a + b;
}
int main() {
cout << "ret = " << func(20, 20) << endl; // 50
cout << "ret = " << func(100) << endl; // 120
cout << "ret = " << func2(20, 20) << endl; // 40
cout << "ret = " << func2(100) << endl; // 110
}
3.2 函数的占位参数 (待补充)
C++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置。
语法:
int func(int a, int b, int) {
// someCode
}
示例:
// 函数占位参数 ,占位参数也可以有默认参数
void func(int a, int) {
cout << "this is func" << endl;
}
int main() {
func(10,10); //占位参数必须填补
}
3.3 函数的重载
C++中,允许出现不同形参的同名函数,接收不同的示参调用不同的函数,提高复用性。
示例1:
// 重载条件
// 1. 同一个作用域下
// 2. 函数名称相同
// 3. 函数参数 类型不同 或者 个数不同 或者 顺序不同
int func(int a, int b) {
return a + b;
}
int func(int a) {
return a;
}
int main() {
cout << func(1,2) << end; // 3 调用了第一个
cout << func(2) << endl; // 2 调用了第二个
}
示例2 (引用作为重载条件):
int func(int &a) {
return a;
}
int func(const int &a) {
return a + 10;
}
int main() {
int a = 10;
cout << func(a) << endl; // 10 调用了无const修饰形参
cout << func(10) << endl; // 20 调用了const修饰形参
}
示例3 (默认参数歧义,const歧义):
// 报错无法运行代码,编译器无法分清下面三种函数的区别
int func(int a, int b = 10) {
return a;
}
int func(const int a) {
return a;
}
int func(int a) {
return a;
}
int main() {
int a = 10;
int const b = 10;
cout << func(a) << endl;
cout << func(b) << endl;
}