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++核心编程(一)相关推荐

  1. 《windows核心编程系列》二谈谈ANSI和Unicode字符集

    第二章:字符和字符串处理 使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是 ...

  2. 《windows核心编程系列》十八谈谈windows钩子

    windows应用程序是基于消息驱动的.各种应用程序对各种消息作出响应从而实现各种功能. windows钩子是windows消息处理机制的一个监视点,通过安装钩子能够达到监视指定窗体某种类型的消息的功 ...

  3. 《Windows核心编程(第5版•英文版)》暨《深入理解.NET(第2版•英文版)》有奖书评/读书笔记征集活动

    <Windows核心编程(第5版•英文版)>暨<深入理解.NET(第2版•英文版)>有奖书评/读书笔记征集活动 图灵公司自成立以来,得到了CSDN的很多专家和朋友的帮助.为了感 ...

  4. SparkSQL核心编程

    目录 基本介绍 DataFrame 创建 DataFrame DataSet 创建 DataSet RDD 转换为 DataSet DataSet 转换为 RDD DataFrame 和 DataSe ...

  5. chHANDLE_DLGMSG(windows核心编程)讲解

    看完<Windows程序设计>后开始看<windows核心编程>, 结果看第一个案例的时候就很惊人的发现,Jeffery大牛的代码很深奥.乍一看好像没有包含<window ...

  6. python 如何判断一个函数执行完成_Python核心编程的四大神兽迭代器、生成器 、闭包以及装饰器...

    本文将主要分为4大部分,分别介绍Python核心编程中的迭代器.生成器 .闭包以及装饰器. 生成器 生成器是生成一个值的特殊函数,它具有这样的特点:第一次执行该函数时,先从头按顺序执行,在碰到yiel ...

  7. 拒绝从入门到放弃_《Python 核心编程 (第二版)》必读目录

    目录 目录 关于这本书 必看知识点 最后 关于这本书 <Python 核心编程 (第二版)>是一本 Python 编程的入门书,分为 Python 核心(其实并不核心,应该叫基础) 和 高 ...

  8. python3.5.0下载-python核心编程最新版下载

    python核心编程是一款专门为Python学习者打造的手机学习软件,同时也是老手编程重要的参考书,为用户提供高级的代码编辑.交互测试.调试等功能,十分的便捷好用,快来下载体验吧. 关于Python: ...

  9. C#学习路线:C#入门经典 -> CLR VIA C# -> WINDOWS核心编程

    C#入门经典:入门阶段 CLR VIA C#:理论基础 WINDOWS核心编程:理论提升

最新文章

  1. oracle23290,oracle标题
  2. 求点到直线的最短距离及垂足
  3. ORACLE纯SQL实现多行合并一行
  4. 《React Native移动开发实战》一一3.4 完善商品列表——ListView组件
  5. FL2440的U-boot-2009.08移植(三)支持Nor FLASH
  6. 1.怎样定制VC#DataGrid列标题?
  7. request.setAttribute()用法
  8. 谣言易碎:诺基亚为何不能投靠Android阵营?
  9. Servlet 过滤器笔记
  10. Kubernetes - - k8s - v1.12.3 持久化部署 GitLab 集成 OpenLDAP 登录
  11. (转)一个由自由职业者建立的量化对冲基金
  12. java初学者代码练习题
  13. 周志华《机器学习》个人笔记
  14. wan端口未连接怎么弄_路由器wan口网线未连接(wan口未插网线)的解决方法
  15. 情人节用python来表白女神
  16. 华为无线portal服务器,portal服务器配置
  17. 读书寄语:感谢揭露你过失的人
  18. 关于赞同科技项目的收获
  19. 接口自动化-第一篇(Python+pytest+allure)
  20. 盘点3个.Net开发的WMS仓库管理系统

热门文章

  1. 2022-2028年中国测绘设备行业研究及前瞻分析报告
  2. Idea SpringBoot 基于 Docker容器环境进行远程调试
  3. Go 知识点(09)— for select 作用于 channel
  4. django自带的分页功能
  5. 命名实体识别NER遗留问题----模型构建
  6. 聚类和EM算法——K均值聚类
  7. 汇编语言关于8086CPU多种寻址方式总结
  8. i.MX6UL: i.MX 6UltraLite处理器 - 低功耗,安全,Arm® Cortex®-A7内核
  9. TensorFlow用法
  10. Cocos 全局变量的使用