c++面向对象概述、内存分析、引用、函数

简介: c++面向对象概述、内存分析、引用、函数

1.c++内存分区模型

c++程序在运行的过程中,内存会被划分为以下四个分区

代码区:程序的所有程序的二进制代码,包括注释会被放到此区

全局区:存放静态变量、全局变量、常量(字符串常量和const修饰的常量),此区的数据将在程序结束后由操作系统释放

using namespace std;
//不在任何函数内的变量是全局变量
int a = 10;
int b = 23;
 
int main() {
  //在某个函数内的变量
  int c = 12;
  cout << (int)&a << endl;
  cout << (int)&b << endl;
  cout << (int)&c << endl;
 
}

栈区:用于存放局部变量、函数参数等,是由编译器自动释放和分配,所以不能让栈区返回一个地址,

#include <iostream>
#include <string>
using namespace std;
 
int* f() {
  int a = 10;
  return &a;//栈区不要返回地址。局部变量是由编译器自动释放和分配
}
int main() {
  
  int* p = f();
  cout << *p;//10 第一次编译器会做保留
  cout << *p;//不会打印10
}

堆区:存储对象(使用new操作符,后面会介绍)由程序员分配释放,若我们不释放,程序结束由操作系统释放

c++中通过new关键字将数据开辟到堆区

#include <iostream>
using namespace std;
int* f() {
  /*
  *1.指针本身也是局部变量,存放在栈长中,但是保存的数据在堆中
  2.new关键字开辟一块堆区,返回的是该数据类型的指针
  */
  int *a = new int(10);
  return a;
}
int main() {
  int* p = f();
  cout << *p;//10
  cout << *p;//10
  //3.堆区的数据程,序员可以通过该delete释放
  delete p;
  //cout << *p;异常
}

如果是new一个数组

//new一个数组
int* arr = new int[10];
//释放一个数组
delete[] arr;

注意

代码区和全局区是程序编译成exe可执行文件的时候就已经有了,但是栈区和堆区是程序exe文件执行后产生的

2.为什么划分内存(内存划分意义)

将不同的数据放在不同的区域,赋予不同的生命周期,提高编程灵活程度

3.引用

使用引用给一个变量起别名

#include <iostream>
using namespace std;
 
int main() {  
  int a = 10;
  /*1.定义引用的格式  数据类型 &别名=原名
  *2.引用必需要初始化
  *3.引用初始化后,不可更改
  */
  int& b = a;
  cout << b;//10
}

前面文章中的地址传递会修改实参,值传递不会修改实参,引用作为函数参数会修改实参,简化使用指针修改实参的复杂过程

#include <iostream>
using namespace std;
void swap(int &a,int &b) {
  int temp = a;
  a = b;
  b = temp;
}
int main() {  
  int a = 10;
  int b = 20;
  swap(a,b);
  cout << a;//20
  cout << b;//10
}

局部变量不能作为函数的返回值返回

#include <iostream>
using namespace std;
int& f() {
  int a = 10;//栈区中的局部变量在函数执行完后释放
  return a;
}
int main() {  
  int &a = f();
  cout << a;//10 编译器保留
  cout << a;//不在是10
}

如果是局部静态变量,可以返回

#include <iostream>
using namespace std;
int& f() {
  static int a = 10;//栈区中的局部变量在函数执行完后释放
  return a;
}
int main() {  
  int &a = f();
  cout << a;//10
  cout << a;//10
}
#include <iostream>
using namespace std;
int& f() {
  static int a = 10;//栈区中的局部变量在函数执行完后释放
  return a;
}
int main() {  
  int &a = f();
  cout << a;//10
  cout << a;//10
  f() = 100;//如果函数的返回是一个引用,可以作为左值
  cout << a;//100
  cout << a;//100
}

引用本质是指针常量

int main() {  
  int a = 1;
  //内部发现是引用,自动转成指针常量 int * const b=&a;
  int& b = a;
  b = 2;//内部发现是引用,自动转成*b=20;
  cout << a;//2
  cout << b;//2
}

常量引用

#include <iostream>
using namespace std;
//使用const修改函数形参,防止误操作
void f(const int& a) {
  //a = 100;不允许修改
}
int main() {  
  int& b = 1;//引用本身需要一个合法内存空间,所以这行代码有误
  int a = 10;
  f(a);
}

 

4.函数相关

前面c++基础系列有关函数知识有所描述,这里补充一些函数高级知识

1.c++中函数可以有默认值

#include <iostream>
using namespace std;
//1.c++中函数可以有默认值,并且某个位置有了默认值,那么从这个位置开始左到右都的有默认值
int f(int a, int b = 10,int c=20) {
  return a + b + c;
}
int main() {  
  int a = 10;
  //2.如果函数有默认值,当我们传值使用传递的值,不传值使用默认的
  cout<<f(a);//40
  cout << f(a, 20);//50
}
//3.声明和实现只能有一个有默认参数
int f1(int a, int b = 10);
int f1(int a, int b) {
  return a + b;
}

2.c++函数中可以有占位参数用来占位,调用函数必需填补该位置

#include <iostream>
using namespace std;
 
//1.只写一个数据类型就是占位
void f(int a,int) {
  cout << "test";
}
//2.占位参数可以有默认值
void f1(int a, int=10) {
  cout << "test";
}
int main() {  
  int a = 10;
  f(a, 10);//占位参必须填补
}

3.函数重载

定义:同一个作用域下,两个函数参数类型不同或者参数顺序不同或者个数不同。此时这两个函数名字可以相同。提高复用性

#include <iostream>
using namespace std;
 
void f(int a,int b) {
  cout << "test";
}
 
void f(int a) {
  cout << "test";
}
int main() {  
  int a = 10;
  f(a, 10);
}

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

4.引用也可作为函数重载条件

#include <iostream>
using namespace std;
 
void f(int &a) {//int &a=10;不合法
  cout << "test";
}
 
void f(const int &a) {//const int &a=10;合法
  cout << "test111";
}
int main() {  
  int a = 10;
  f(a);//test
  f(10);//test111
}

5.函数重载遇到默认参数需要注意

void f(int a,int b=10) {//int &a=10;不合法
  cout << "test";
}
 
void f(int a) {//const int &a=10;合法
  cout << "test111";
}
int main() {  
  int a = 10;
  f(a);//报错,出现二义性
}



相关文章
|
16天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
11天前
|
开发框架 监控 .NET
【Azure App Service】部署在App Service上的.NET应用内存消耗不能超过2GB的情况分析
x64 dotnet runtime is not installed on the app service by default. Since we had the app service running in x64, it was proxying the request to a 32 bit dotnet process which was throwing an OutOfMemoryException with requests >100MB. It worked on the IaaS servers because we had the x64 runtime install
|
12天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
35 4
|
21天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
135 9
|
25天前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
1月前
|
程序员 C++ 容器
在 C++中,realloc 函数返回 NULL 时,需要手动释放原来的内存吗?
在 C++ 中,当 realloc 函数返回 NULL 时,表示内存重新分配失败,但原内存块仍然有效,因此需要手动释放原来的内存,以避免内存泄漏。
|
1月前
|
存储 C语言 C++
【C++打怪之路Lv6】-- 内存管理
【C++打怪之路Lv6】-- 内存管理
38 0
【C++打怪之路Lv6】-- 内存管理
|
10天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
39 4
|
1月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
27 4
|
1月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
23 4
下一篇
无影云桌面