C++核心编程(一)
C++ 核心编程
本系类列博客都是根据黑马的C++视频做的笔记。
本阶段主要针对C++面向对象编程技术做详细记录,探讨C++中的核心和精髓。
1、内存分区模型
C++程序在执行时,将内存大方向划分为4个区域
- 代码区:存放函数体的二进制代码,由操作系统进行管理
- 全局区:存放全局变量和静态变量以及常量
- 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等
- 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收
内存四区的意义
不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程
1.1 程序运行前
在程序编译后,生成了exe可执行程序,未执行该程序前分为两个区域:
- 代码区
存放CPU执行的机器指令
代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可
代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令 - 全局区
全局变量和静态变量存放于此
全局区还包含了常量区,字符串常量和其他常量也存放于此
该区域的数据在程序结束后由操作系统释放
const
修饰的变量也是常量
示例
#include <iostream>using namespace std;// 定义全局变量
int g_a = 10;
int g_b = 10;// 定义const修饰全局常量
const int c_g_a = 10;
const int c_g_b = 10;int main()
{// 全局区// 全局变量、静态变量、常量// 创建一个普通的局部变量int a = 10;int b = 10;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;static int s_b = 10;cout << "静态变量s_a的地址为:" << (int)&s_a << endl;cout << "静态变量s_b的地址为:" << (int)&s_b << endl;// 常量// 字符串常量cout << "字符串'Hello world'的地址为:" << (int)&("Hello world") << endl;// const修饰的变量// const 修饰的全局变量,const修饰的局部变量cout << "全部常量c_g_a的地址为:" << (int)&c_g_a << endl;cout << "全部常量c_g_b的地址为:" << (int)&c_g_b << endl;// 局部const int c_l_a = 10; // c const; g global l localconst int c_l_b = 10;cout << "局部常量c_l_a的地址为:" << (int)&c_l_a << endl;cout << "局部常量c_l_b的地址为:" << (int)&c_l_b << endl;return 0;}
总结
- C++中在程序运行之前分为
全局区
和代码区
- 代码区特点是
共享
和只读
- 全局区中存放
全局变量
、静态变量
、常量
- 常量区中存放
const
修饰的全局变量
和字符串常量
1.2 程序运行后
1.2.1 栈区
由编译器自动分配释放,存放函数的参数值,局部变量等
注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放
示例
#include <iostream>using namespace std;// 栈区数据注意事项,不要反悔局部变量的地址
// 栈区的数据由编译器管理开辟和释放int* func(int b) // 形参数据也会保留在栈区
{int a = 10; // 局部变量,存放在栈区,栈区的数据在函数执行完成之后自动释放return &a; // 返回局部变量的地址
}int main()
{// 接受func函数的返回值int* p = func(2);cout << *p << endl; // 本次可能打印正确的数据,编译器做了保留cout << *p << endl; // 第二次这个数据就不再保留了return 0;
}
1.2.3 堆区
- 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收
- 在c++中主要利用
new
在堆去开辟内存
示例
#include <iostream>
using namespace std;int* func()
{// 利用new关键字可以将数据开辟到堆区// new 关键字返回的是地址// 指针本质也是一个变量(局部变量),放在栈上,指针指向的数据是放在堆区int *a = new int(10);return a;
}int main()
{// 在堆区中开辟数据int* p = func();cout << *p << endl;cout << *p << endl;cout << *p << endl;return 0;
}
总结
堆区的数据由程序员管理开辟和释放
堆区的数据利用关键字new
进行开辟内存
1.3 new操作符
C++中利用new
操作符在堆区开辟数据
堆区开辟的数据,有程序员手动开辟,手动释放利用操作符delete
语法:new 数据类型
利用new
创建的数据会返回对应数据类型的指针
示例
#include <iostream>
using namespace std;int* func()
{// 在堆区创建整型数据// new 返回的是 该数据类型的指针int* p = new int(10);return p;
}void test01()
{int* p = func();cout << *p << endl;cout << *p << endl;// 堆区的数据由程序员管理,包括开辟和释放// 释放堆区中的数据,利用关键字newdelete p;//cout << *p << endl; // 内存已经被释放,再次访问就是非法操作,就会报错
}// 2、在堆区利用new开辟一个数组
void test02()
{// 创建10整型的数据的数组,在堆区int* arr = new int[10]; // 10代表数组里面有10个元素for (int i = 0; i <10; i++){arr[i] = i + 1;}for (int j= 0; j < 10; j++){cout << arr[j] << endl;}// 释放堆区数组// 释放数组的伤害,要加 []才可以delete[] arr;}
int main()
{//test01();test02();return 0;
}
注意
数组的释放需要加[]
才可以,例如:delete[] arr
2、引用
2.1 引用的基本使用
**作用:**给变量取别名
**语法:**数据类型 &别名 = 原名
图示:
示例
#include <iostream>using namespace std;int main()
{// 引用的基本语法// 数据类型 &别名 = 原名int a = 10;// 创建引用int& b = a;cout << "a = " << a << endl;cout << "b = " << b << endl;// 修改值b = 100;cout << "a = " << a << endl;cout << "b = " << b << endl;return 0;
}
2.2 引用注意事项
- 引用必须要初始化
- 引用一旦初始化就不可以更改
图示
示例
#include <iostream>
using namespace std;int main()
{int a = 10;// 1. 引用必须初始化//int& b; // 错误,必须要初始化int& b = a;// 2、 引用在初始化后,不可以改变int c = 20;b = c; // 此为赋值操作,并不是更改引用cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;}
2.3 引用做函数的参数
作用: 函数传参是,可以利用引用的技术让形参修饰实参
优点: 可以简化指针修改实参
示例
#include<iostream>
using namespace std;// 1、值传递
void mySwap01(int a, int b)
{int temp = a;a = b;b = temp;
}// 2、地址传递
void mySwap02(int* a, int* b)
{int temp = *a;*a = *b;*b = temp;}// 3、 引用传递
void mySwap03(int& a, int& b)
{int temp = a;a = b;b = temp;
}int main()
{int a = 10;int b = 20;cout << "a = " << a << endl;cout << "b = " << b << endl;// mySwap01(a, b); // 未交换// mySwap02(&a, &b); // 已经交互mySwap03(a, b); // 已经交换cout << "a = " << a << endl;cout << "b = " << b << endl;return 0;
}
总结:通过引用参数产生的效果和按地址传递是一样的。引用的语法更清楚简单。
2.4 引用做函数的返回值
作用: 引用是I可以作为函数的返回值存在的
注意:不要返回局部变量引用
用法:函数调用作为左值
示例
#include<iostream>
using namespace std;// 引用做函数的返回值
// 1、不要反悔局部变量的引用
int& test01()
{int a = 10; // 局部变量存放在四区中的栈区return a;
}// 2、函数调用可以可以作为左值
int& test02()
{static int a = 10; // 静态变量,存放在全局区,在程序结束后释放return a;
}int main()
{//int& ref = test01();//cout << "ref = " << ref << endl; // 第一次 正确的数据 编译器做了保留//cout << "ref = " << ref << endl; // 第二次 错误结果 a的内存已经释放 无权操作int& ref2 = test02();cout << "ref2 = " << ref2 << endl;cout << "ref2 = " << ref2 << endl;// 左值test02() = 1000;cout << "ref2 = " << ref2 << endl;cout << "ref2 = " << ref2 << endl;return 0;
}
2.5 引用的本质
本质: 引用的本质在c++中就是一个指针常量
讲解示例:
// 发现是引用,转换为 int* const ref = &a;
void func(int& ref)
{ref = 200;
}int main()
{int a = 10;// 自动转换为 int* const ref = &a;指针常量是指针指向不可更改int& ref = a;ref = 20; // 内部发现ref是引用,自动帮我们转换为 *ref = 20;cout << "a = " << a << endl;cout << "ref = " << ref << endl;func(a);return 0;}
图示
结论:C++推荐使用引用计数,因为语法方便,引用本质就是指针常量,但是所有的指针操作编译器都帮我们做了。
2.6 常量引用
作用: 主要用于修饰形参,防止误操作
在函数形参列表中,可以加const
修饰形参,防止形参改变实参
示例:
// 打印数据
void showValue(const int& val)
{// val = 10000; // 防止被修改cout << "val = " << val << endl;
}int main()
{// 常量引用// 使用场景:用来修饰形参,防止误操作int a = 10;// 加上const之后,编译器将代码修改为 int temp = 10; const int& ref = temp;// 如果没有const 也即:int& ref = 10; 为错误语法const int& ref = 10; // 引用必须引用一块合法的内存空间// ref = 20; // 加入const之后变为只读,不可写showValue(a);return 0;
}
3、函数提高
3.1 函数默认参数
在C++中,函数的形参列表中的形参是可以有默认值得。
语法:返回值类型 函数名(参数 = 默认值){}
示例:
#include<iostream>
using namespace std;// 函数默认参数
int sum(int a = 0, int b = 0, int c = 0)
{return a + b + c;
}// 注意事项:、
// 1、如果某个位置已经有了默认参数,那么这个位置之后,都必须要有默认值
// 2、如果函数声明有默认参数,函数实现就不能有默认参数,如果函数实现有默认参数,声明就不能有参数int func(int a = 10, int b = 20, int c = 30);
int func(int a, int b, int c)
{return a + b + c;
}int main()
{cout << sum() << endl;cout << sum(10, 20) << endl;cout << sum(10, 20, 20) << endl;cout << func(20, 100) << endl;return 0;
}
注意: 如果某个位置有了默认参数,后面的参数必须都要设定默认值,例如:int sum(int a = 0, int b)
为错误使用。
注意: 如果函数声明有默认参数,函数实现就不能有默认参数,如果函数实现有默认参数,声明就不能有参数
结论: 如果传入参数,就用传入的值,如果没有,就是用默认值
3.2 函数占位符
C++中函数的形参列表里面可以有占位参数,用来占位,调用函数时必须填补该位置
语法: 返回值类型 函数名(数据类型){}
在现阶段函数的占位参数存在意义不大,但是后面的课程中会用到该特性
示例:
// 占位参数
// 返回值类型 函数名(数据类型) {}// 目前阶段的占位参数 还用不到,后面知识点中会用到
// 占位参数还可以有默认参数
void func(int a, int = 10)
{cout << "this is func " << endl;
}int main()
{// 如果不给第二个占位参数传值,则报错func(10, 10);return 0;
}
3.3 函数的重载
3.3.1 函数重载概述
作用: 函数名可以相同,提高复用性
函数重载满足条件:
- 同一个作用域下
- 函数名相同
- 函数参数的类型不同、个数不同、或者顺序不同
注意: 函数的返回值不可以作为函数重载的条件
示例:
#include<iostream>
using namespace std;// 函数重载
// 可以让函数名相同,提高复用性// 函数重载条件
// 1、同一个作用域
// 2、同一个函数名
// 3、参数的类型、个数以及类型不同
void func()
{cout << "func 的调用!" << endl;
}void func(int a)
{cout << "func 的调用!!" << endl;
}void func(double a)
{cout << "func double 的调用!!" << endl;
}void func(int a, double b)
{cout << "func(int, double) 的调用!!" << endl;
}void func(double b, int a)
{cout << "func(double, a) 的调用!!!" << endl;
}// 函数的返回值不可以作为重载条件
int main()
{// 如果不给第二个占位参数传值,则报错func();func(20);func(2.1);func(10.2, 20);func(20, 10.2);return 0;
}
3.3.2 函数重载注意事项
- 引用作为重载条件
- 函数重载遇到函数默认参数
示例:
#include<iostream>
using namespace std;// 函数重载的注意事项
// 1、应用作为重载的条件
void func(int &a) // int &a = 10; 不合法
{cout << "func(int &a) 的调用!" << endl;
}void func(const int &a) // const int &a = 10 合法
{cout << "func(const int& a) 的调用!" << endl;
}// 2、函数重载碰到默认参数
void func2(int a)
{cout << "func2(int a) 的调用!" << endl;
}// 默认参数会导致二义性,和上面的函数有冲突
//void func2(int a, int b = 20)
//{// cout << "func2(int a = 2) 的调用!" << endl;
//}// 2、函数重载遇到默认参数
// 函数的返回值不可以作为重载条件
int main()
{// 如果不给第二个占位参数传值,则报错int a = 10;func(a); // 调用的是:func(int& a)函数func(10); // 调用的是:func(const int& a)函数func2(2);return 0;
}
注意: 函数形参有默认值时,可能会导致函数二义性,也即调用函数不知道该调用哪一个函数,应当尽量避免这种情况。
C++核心编程(一)相关推荐
- 《windows核心编程系列》二谈谈ANSI和Unicode字符集
第二章:字符和字符串处理 使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是 ...
- 《windows核心编程系列》十八谈谈windows钩子
windows应用程序是基于消息驱动的.各种应用程序对各种消息作出响应从而实现各种功能. windows钩子是windows消息处理机制的一个监视点,通过安装钩子能够达到监视指定窗体某种类型的消息的功 ...
- 《Windows核心编程(第5版•英文版)》暨《深入理解.NET(第2版•英文版)》有奖书评/读书笔记征集活动
<Windows核心编程(第5版•英文版)>暨<深入理解.NET(第2版•英文版)>有奖书评/读书笔记征集活动 图灵公司自成立以来,得到了CSDN的很多专家和朋友的帮助.为了感 ...
- SparkSQL核心编程
目录 基本介绍 DataFrame 创建 DataFrame DataSet 创建 DataSet RDD 转换为 DataSet DataSet 转换为 RDD DataFrame 和 DataSe ...
- chHANDLE_DLGMSG(windows核心编程)讲解
看完<Windows程序设计>后开始看<windows核心编程>, 结果看第一个案例的时候就很惊人的发现,Jeffery大牛的代码很深奥.乍一看好像没有包含<window ...
- python 如何判断一个函数执行完成_Python核心编程的四大神兽迭代器、生成器 、闭包以及装饰器...
本文将主要分为4大部分,分别介绍Python核心编程中的迭代器.生成器 .闭包以及装饰器. 生成器 生成器是生成一个值的特殊函数,它具有这样的特点:第一次执行该函数时,先从头按顺序执行,在碰到yiel ...
- 拒绝从入门到放弃_《Python 核心编程 (第二版)》必读目录
目录 目录 关于这本书 必看知识点 最后 关于这本书 <Python 核心编程 (第二版)>是一本 Python 编程的入门书,分为 Python 核心(其实并不核心,应该叫基础) 和 高 ...
- python3.5.0下载-python核心编程最新版下载
python核心编程是一款专门为Python学习者打造的手机学习软件,同时也是老手编程重要的参考书,为用户提供高级的代码编辑.交互测试.调试等功能,十分的便捷好用,快来下载体验吧. 关于Python: ...
- C#学习路线:C#入门经典 -> CLR VIA C# -> WINDOWS核心编程
C#入门经典:入门阶段 CLR VIA C#:理论基础 WINDOWS核心编程:理论提升
最新文章
- oracle23290,oracle标题
- 求点到直线的最短距离及垂足
- ORACLE纯SQL实现多行合并一行
- 《React Native移动开发实战》一一3.4 完善商品列表——ListView组件
- FL2440的U-boot-2009.08移植(三)支持Nor FLASH
- 1.怎样定制VC#DataGrid列标题?
- request.setAttribute()用法
- 谣言易碎:诺基亚为何不能投靠Android阵营?
- Servlet 过滤器笔记
- Kubernetes - - k8s - v1.12.3 持久化部署 GitLab 集成 OpenLDAP 登录
- (转)一个由自由职业者建立的量化对冲基金
- java初学者代码练习题
- 周志华《机器学习》个人笔记
- wan端口未连接怎么弄_路由器wan口网线未连接(wan口未插网线)的解决方法
- 情人节用python来表白女神
- 华为无线portal服务器,portal服务器配置
- 读书寄语:感谢揭露你过失的人
- 关于赞同科技项目的收获
- 接口自动化-第一篇(Python+pytest+allure)
- 盘点3个.Net开发的WMS仓库管理系统