(黑马)C++核心编程笔记(上)

简介: (黑马)C++核心编程笔记

1 内存分区模型


C++程序在执行时,将内存大方向分为4个区域


  • 代码区:存放函数体的二进制代码,由操作系统进行管理的


  • 全局区:存放全局变量和静态变量以及常量


  • 栈区:由编译器自动分配释放,存放函数的参数值,局部变量等


  • 堆区:由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收


内存四区意义:


  • 不同区域存放的数据,赋予不同的生命周期,给我们更大的灵活编程


1.1 程序运行前


在程序编码后,生成了exe可执行程序,未执行该程序前分为两个区域


代码区:


  • 存放CPU执行的机器指令


  • 代码区是共享的,共享的目的是对于频繁被执行的程序,只需要在内存中有一份代码即可


  • 代码区是只读的,使其只读的原因是防止程序意外地修改了它的指令


全局区:


  • 全局变量和静态变量存放在此


  • 全局区还包含了常量区,字符串常量和其他常量也存放在此


  • 该区域的数据在程序结束后由操作系统释放



#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,属于静态变量
  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 << "字符串常量的地址为:" << (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修饰的局部变量
  const int c_l_a = 10;    //
  const int c_l_b = 10;
  cout << "局部常量c_l_a的地址为:" << (int)&c_l_a << endl;
  cout << "局部常量c_l_b的地址为:" << (int)&c_l_b << endl;
  system("pause");
  return 0;
}


局部变量a的地址为:7601824
局部变量b的地址为:7601812
全局变量g_a的地址为:12173364
全局变量g_b的地址为:12173368
静态变量s_a的地址为:12173372
静态变量s_b的地址为:12173376
字符串常量的地址为:12163932
全局常量c_g_a的地址为:12164744
全局常量c_g_b的地址为:12164748
局部常量c_l_a的地址为:7601800
局部常量c_l_b的地址为:7601788
请按任意键继续. . .


总结:


  • C++中在程序运行前分为全局区和代码区


  • 代码区特点是共享和只读


  • 全局区中存放全局变量、静态变量、常量


  • 常量区中存放const修饰的全局常量和字符串常量


1.2 程序运行后


栈区:


  • 有编译器自动分配释放,存放函数的参数值,局部变量等


  • 注意事项:不要返回局部变量的地址,栈区开辟的数据由编译器自动释放


#include<iostream>
using namespace std;
//栈区数据注意事项  ---不要返回局部变量的地址
//栈区的数据由编译器管理开辟和释放
//int* func(int b)  //形参数据也会放在栈区
int* func()
{
  //b = 100;
  int a = 10;  //局部变量  存放在栈区,栈区的数据在函数执行完后自动释放
  return &a;   //返回局部变量的地址
}
int main()
{
  //接受func函数的返回值
  int* p = func();
  cout << *p << endl;   //第一次可以打印正确的数字,是因为编译器做了保留
  cout << *p << endl;   //第二次输出错误
  system("pause");
  return 0;
}


10
2038286832
请按任意键继续. . .


堆区:


  • 由程序员分配释放,若程序员不释放,程序结束时由操作系统回收


  • 在C++中主要利用new在堆区开辟内存


#include<iostream>
using namespace std;
int * func()
{
  //利用new关键字 可以将数据开辟到堆区
  //指针 本质也是局部变量,放在栈上,指针保存的数据是放在堆区
  int * p = new int(10);
  return p;    //返回地址
}
int main()
{
  //在堆区开辟数据
  int *p = func();   //接受地址
  cout << *p << endl;
  cout << *p << endl;
  system("pause");
  return 0;
}


10
10
请按任意键继续. . .


总结:


  • 堆区数据由程序员管理开辟和释放


  • 堆区数据利用new关键字进行开辟内存


1.3 new操作符


  • C++中利用new操作符在堆区开辟数据


  • 堆区开辟的数据,由程序员手动开辟,手动释放,释放利用操作符delete


  • 语法:new 数据类型


  • 利用new创建的数据,会返回该数据对应的类型的指针


#include<iostream>
using namespace std;
//1、new的基本语法
int* func()
{
  //在堆区创建整型数据
  //new返回时 该数据类型的指针
  int* p = new int(10);
  return p;   //返回地址
}
void test01()
{
  int* p = func();
  cout << *p << endl;
  cout << *p << endl;
  //堆区的数据 由程序员管理开辟,程序员管理释放
  //如果想释放堆区的数据,利用关键字delete
  delete 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 + 100;  //给10个元素赋值  100-109
  }
  for (int i = 0; i < 10; i++)
  {
    cout << arr[i] << endl;
  }
}
int main()
{
  test01();
  test02();
  system("pause");
  return 0;
}


10
10
100
101
102
103
104
105
106
107
108
109
请按任意键继续. . .


2 引用


2.1 引用的基本使用


作用: 给变量起别名


语法:数据类型 &别名 = 原名


#include<iostream>
using namespace std;
int main()
{
  //引用基本语法
  //数据类型 &别名 = 原名
  int a = 10;
  int &b = a;      //a起别名为b
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  b = 100;
  cout << "修改b之后:" << endl;    //修改别名存储的数据,原数据跟着改变
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  system("pause");
  return 0;
}


a=10
b=10
修改b之后:
a=100
b=100
请按任意键继续. . .


2.2 引用注意事项


  • 引用必须初始化


  • 引用在初始化后,不可以改变(b称为a的别名之后,不能再是c的别名


#include<iostream>
using namespace std;
int main()
{
  int a = 10;
  //1、引用必须初始化
  int& b = a;    //初始化为a
  //2、引用在初始化后,不可以改变
  int c = 20;
  b = c;  //赋值操作,而不是更改引用
  //int& b = c;
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  cout << "c=" << c << endl;
  system("pause");
  return 0;
}


a=20
b=20
c=20
请按任意键继续. . .


2.3 引用做函数参数


作用:函数传参时,可以利用引用的技术让形参修饰实参


优点:可以简化指针修改实参


#include<iostream>
using namespace std;
//交换函数
//1、值传递
void mySwop01(int a, int b)
{
  int temp = a;
  a = b;
  b = temp;
  cout << "swap01 a=" << a << endl;
  cout << "swap02 b=" << b << endl;
}
//2、地址传递
void mySwop02(int* a, int* b)
{
  int temp = *a;
  *a = *b;
  *b = temp;
}
//3、引用传递
void mySwop03(int& a, int& b)
{
  int temp = a;
  a = b;
  b = temp;
}
int main()
{
  int a = 10;
  int b = 20;
  //mySwop01(a, b);   //实参未改变,形参改变
  //mySwop02(&a, &b);   //实参为地址
  mySwop03(a, b);
  cout << "a=" << a << endl;
  cout << "b=" << b << endl;
  system("pause");
  return 0;
}


a=20
b=10
请按任意键继续. . .


总结:通过引用参数产生的效果同按地址传递是一样的。引用的语法更清楚简单。


2.4 引用做函数返回值


作用:引用是可以作为函数的返回值存在的


注意:不要返回局部变量引用


用法:函数调用作为左值


#include<iostream>
using namespace std;
//引用做函数的返回值
//1、不要返回局部变量的引用
int& test01()    //相当于现在test01()函数是  a的别名了,a可以做左值,函数当然也可以
{
  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;   //ref2是a的别名
  test02() = 1000;   //如果函数的返回值是引用,这个函数调用可以作为左值
  cout << "ref2=" << ref2 << endl;
  cout << "ref2=" << ref2 << endl;
  system("pause"); 
  return 0;
}


ref=10
ref=2059717104
ref2=10
ref2=10
ref2=1000
ref2=1000
请按任意键继续. . .


2.5 引用的本质


本质:引用的本质在C++内部实现是一个指针常量


2.6 常量引用


作用:常量引用主要用来修饰形参,防止误操作


在函数形参列表中,可以加const修饰形参,防止形参改变实参


#include<iostream>
using namespace std;
//打印数据的函数
void showValue(const int& val)
{
  //val = 1000;  //加了const就不可修改
  cout << "val = " << val << endl;
}
int main()
{
  //常量引用
  //使用场景:用来修饰形参,防止误操作
  //int a = 10;
  //加上const之后, 编译器将代码修改 int temp = 10;  const int& ref= temp
  const int& ref = 10;  //引用必须引一个合法的内存空间
  //ret = 20;   //加入const之后变为只读,不可修改
  int a = 100;
  showValue(a);   //a起别名为val
  cout << "a = " << a << endl;
  system("pause"); 
  return 0;
}


val = 100
a = 100
请按任意键继续. . .


3 函数提高


3.1 函数默认参数


  • 在C++中,函数的形参列表中的形参是可以有默认值的


  • 语法:返回值类型 函数名 (参数=默认值){ }


#include<iostream>
using namespace std;
//函数默认参数
int func(int a, int b, int c)
{
  return a + b + c;
}
//如果我们自己传入数据,就用自己的数; 没有则用默认值
int func1(int a, int b=20, int c=30)   //默认参数
{
  return a + b + c;
}
//注意事项
//1、如果某个位置已经有了默认参数,那么从这个位置之后,从左到右必须有默认值
//2、如果函数声明有默认参数,函数实现就不能有默认参数
int func2(int a = 10, int b = 10, int c = 10);    //函数声明
int func2(int a, int b, int c)                    //函数实现
//声明和实现只能有一个有默认参数
{
  return a + b + c;
}
int main()
{
  cout << func(10, 20, 30) << endl;
  cout << func1(20) << endl;
  cout << func1(10, 10) << endl;
  cout << func2() << endl;
  system("pause"); 
  return 0;
}


60
70
50
30
请按任意键继续. . .


3.2 函数占位参数


  • C++中函数的形参列表中可以有占位参数,用来做占位,调用函数时必须填补该位置。


  • 语法:返回值类型 函数名 (数据类型){}


在现阶段函数的占位参数存在意义不大,但是后面的课程中会用到该作用


#include<iostream>
using namespace std;
//占位参数
//返回值类型 函数名(数据类型) {}
//目前阶段的占位参数还用不到,后面的课程中会用到
//占位参数 还可以有默认参数
void func(int a, int=10)
{
  cout << "this is func" << endl;
}
int main()
{
  func(10, 10);
  system("pause"); 
  return 0;
}


this is func
请按任意键继续. . .


3.3 函数重载


3.3.1 函数重载概述


作用:函数名可以相同,提高复用性


函数重载满足条件:


  • 同一个作用域下


  • 函数名称相同


  • 函数参数类型不同 或者 个数不同 或者 顺序不同


注意:函数的返回值不可以作为函数重载的条件


#include<iostream>
using namespace std;
//函数重载
//可以让函数名相同,提高复用性
//函数重载的满足条件
//1、同一个作用域下
//2、函数名称相同
//3、函数的参数类型不同  或 个数不同 或 顺序不同
void func()
{
  cout << "func调用" << endl;
}
void func(int a)     
{
  cout << "func(int a)的调用!" << endl;
}
void func(double a)     //参数类型不同
{
  cout << "func(double a)的调用!" << endl;
}
void func(int a, double b)   //参数个数不同
{
  cout << "func(int a, double b)的调用!" << endl;
}
void func(double a, int b)   //参数顺序不同
{
  cout << "func(double a, int b)的调用!" << endl;
}
//注意事项
//函数的返回值不可以作为函数重载的条件
//int func(double a, int b)   //参数顺序不同
//{
//  cout << "func(double a, int b)的调用!" << endl;
//}
int main()
{
  func();
  func(10);
  func(3.14);
  func(1, 1.1);
  func(1.1, 1);
  system("pause"); 
  return 0;
}


func调用
func(int a)的调用!
func(double a)的调用!
func(int a, double b)的调用!
func(double a, int b)的调用!
请按任意键继续. . .


3.3.2 函数重载注意事项


  • 引用作为重载条件


  • 函数重载碰到函数默认参数


#include<iostream>
using namespace std;
//函数重载的注意事项
//1、引用作为重载的条件
void func(int& a)
{
  cout << "func(int& a)调用" << endl;
}
void func(const int& a)   //类型不同
{
  cout << "func(const int& a)调用" << endl;
}
//2、函数重载碰到默认参数
void func2(int a)
{
  cout << "func2(int a)的调用" << endl;
}
//void func2(int a, int b=10)
//{
//  cout << "func2(int a, int b=10)的调用" << endl;
//}
int main()
{
  int a = 10;
  func(a);    //调用没有const的函数
  func(10);    //调用有const的函数
  func2(10);   //当函数重载碰到默认参数,出现二义性,报错,尽量避免这种情况
  system("pause"); 
  return 0;
}


func(int& a)调用
func(const int& a)调用
func2(int a)的调用
请按任意键继续. . .


4 类和对象


  • C++面向对象的三大特性为:封装、继承、多态


  • C++认为万事都皆为对象,对象上有其属性和行为


例如:


人可以作为对象,属性有姓名、年龄、身高、体重…,行为有走、跳、跑、吃饭…


车也可以作为对象,属性有轮胎、方向盘、车灯…,行为有载人、放音乐、放空调…


具有相同性质的对象,我们可抽象为类,人属于人类,车属于车类


4.1 封装


4.1.1 封装的意义


封装是C++面向对象的三大特性之一


封装的意义:


  • 将属性和行为作为一个整体,表现生活中的事物


  • 将属性和行为加以权限控制


封装的意义一:


  • 在设计类的时候,属性和行为写在一起,表现事物


语法:class 类名{访问权限:属性/行为};


#include<iostream>
using namespace std;
//设计一个圆类,求圆的周长
//圆求周长的公式:2*pi*r
const double PI = 3.14;
class Circle
{
  //访问权限
  //公共权限
public:
  //属性
  //半径
  int m_r;
  //行为
  //获取圆的周长
  double calculateZC()
  {
    return 2 * PI * m_r;
  }
};
int main()
{
  //通过圆类 创建具体的圆(对象)
  Circle c1;    //实例化
  //给圆对象 的属性进行复制
  c1.m_r = 10;
  cout << "圆的周长为:" << c1.calculateZC() << endl;
  system("pause"); 
  return 0;
}


圆的周长为:62.8
请按任意键继续. . .


示例2:设计一个学生类,属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号


#include<iostream>
using namespace std;
#include<string>
//设计一个学生类,属性有姓名和学号
//可以给姓名和学号赋值,可以显示学生的姓名和学号
//设计学生类
class Student
{
  //类中的属性和行为  我们统一称为 成员
  //属性: 成员属性  成员变量
  //行为  成员函数  成员方法
  //访问权限
  //公共权限
public:
  //属性
  //姓名
  string name;
  //学号
  string id;
  //行为
  //显示姓名和学号
  void showStudent()
  {
    cout << "姓名:" << name << endl;
    cout << "学号:" << id << endl;
  }
  void setName(string a_name)   //行为给属性赋值
  {
    name = a_name;
  }
  void setID(string a_id)
  {
    id = a_id;
  }
};
int main()
{
  Student std1;         //实例化
  std1.name = "zzz";    //赋值
  std1.id = "123456";
  std1.showStudent();
  //Student std2;
  std1.setName("张三");
  std1.setID("222222");
  std1.showStudent();
  system("pause");
  return 0;
}


封装意义二:


  • 类在设计时,可以把属性和行为放在不同的权限下,加以控制


访问权限有三种:


  • 1、public :公共权限


  • 2、protected:保护权限


  • 3、private:私有权限


#include<iostream>
using namespace std;
//访问权限 : 三种
//1、公共权限  public       成员类内可以访问,类外也可以访问
//2、保护权限 protected     成员类内可以访问  类外不可以访问   子类也可以访问
//3、私有权限 private       成员类内可以访问  类外不可以访问   子类不可以访问
class Person
{
public:
  //公共权限
  string m_Name;
  void func()
  {
    m_Name = "张三";    //类内可以访问
    m_Car = "拖拉机";
    m_Password = 123456;
  }
  void show()
  {
    cout << "姓名:" << m_Name << endl;
    cout << "车:" << m_Car << endl;
    cout << "密码:" << m_Password << endl;
  }
protected:
  //保护权限
  string m_Car;
private:
  //私有权限
  int m_Password;
};
int main()
{
  Person p1;
  p1.func();
  p1.show();
  cout << "修改之后------------" << endl;
  p1.m_Name = "李四";
  cout << "姓名:" << p1.m_Name << endl;
  //下面都报错,不能访问
  //cout << "车:" << p1.m_Car << endl;
  //p1.m_Car = "奔驰";
  //p1.m_Password = 123;
  system("pause");
  return 0;
}


目录
相关文章
|
5月前
|
C++
C++ 语言异常处理实战:在编程潮流中坚守稳定,开启代码可靠之旅
【8月更文挑战第22天】C++的异常处理机制是确保程序稳定的关键特性。它允许程序在遇到错误时优雅地响应而非直接崩溃。通过`throw`抛出异常,并用`catch`捕获处理,可使程序控制流跳转至错误处理代码。例如,在进行除法运算或文件读取时,若发生除数为零或文件无法打开等错误,则可通过抛出异常并在调用处捕获来妥善处理这些情况。恰当使用异常处理能显著提升程序的健壮性和维护性。
89 2
|
3月前
|
存储 C++ UED
【实战指南】4步实现C++插件化编程,轻松实现功能定制与扩展
本文介绍了如何通过四步实现C++插件化编程,实现功能定制与扩展。主要内容包括引言、概述、需求分析、设计方案、详细设计、验证和总结。通过动态加载功能模块,实现软件的高度灵活性和可扩展性,支持快速定制和市场变化响应。具体步骤涉及配置文件构建、模块编译、动态库入口实现和主程序加载。验证部分展示了模块加载成功的日志和配置信息。总结中强调了插件化编程的优势及其在多个方面的应用。
438 67
|
5月前
|
算法 C语言 C++
C++语言学习指南:从新手到高手,一文带你领略系统编程的巅峰技艺!
【8月更文挑战第22天】C++由Bjarne Stroustrup于1985年创立,凭借卓越性能与灵活性,在系统编程、游戏开发等领域占据重要地位。它继承了C语言的高效性,并引入面向对象编程,使代码更模块化易管理。C++支持基本语法如变量声明与控制结构;通过`iostream`库实现输入输出;利用类与对象实现面向对象编程;提供模板增强代码复用性;具备异常处理机制确保程序健壮性;C++11引入现代化特性简化编程;标准模板库(STL)支持高效编程;多线程支持利用多核优势。虽然学习曲线陡峭,但掌握后可开启高性能编程大门。随着新标准如C++20的发展,C++持续演进,提供更多开发可能性。
94 0
|
3月前
|
安全 程序员 编译器
【实战经验】17个C++编程常见错误及其解决方案
想必不少程序员都有类似的经历:辛苦敲完项目代码,内心满是对作品品质的自信,然而当静态扫描工具登场时,却揭示出诸多隐藏的警告问题。为了让自己的编程之路更加顺畅,也为了持续精进技艺,我想借此机会汇总分享那些常被我们无意间忽视却又导致警告的编程小细节,以此作为对未来的自我警示和提升。
392 11
|
2月前
|
消息中间件 存储 安全
|
3月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
69 2
|
4月前
|
存储 算法 C++
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
文章详细探讨了C++中的泛型编程与STL技术,重点讲解了如何使用模板来创建通用的函数和类,以及模板在提高代码复用性和灵活性方面的作用。
68 2
C++提高篇:泛型编程和STL技术详解,探讨C++更深层的使用
|
3月前
|
安全 程序员 编译器
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
【C++篇】继承之韵:解构编程奥义,领略面向对象的至高法则
97 11
|
3月前
|
存储 编译器 C++
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
【C++篇】引领C++模板初体验:泛型编程的力量与妙用
55 9
|
3月前
|
存储 C++ 容器
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器1
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
76 5