首页 > 系统相关 >c++定义了类在main函数中使用的一个坑现象的解决,让我理解了栈,堆和内存之间关系。

c++定义了类在main函数中使用的一个坑现象的解决,让我理解了栈,堆和内存之间关系。

时间:2024-06-13 13:21:18浏览次数:18  
标签:定义 c++ 内存 使用 空间 new main delete

首先描述一下我的坑是啥?我的坑就是写了一个对集料颗粒进行角度计算的类,在main函数中使用采用了类定义申明,这样使用导致一个坑,这个类中对于集料的数目进行了宏定义,发现数据如果超过20个,编译就报错,当时没有太在意这个坑,没有思考什么原因。也就将就者用了。

后来对接同事说,这个颗粒数目不能限制在20个,至少要100个。说我的类里面太多静态数组,这个消耗太多的内存空间了。需要换一种方法,改成动态方式,有多少就申请多少。我估计者这个类里面成员变量修改动作太大,还不如直接将类定义申明,修改成new方式动态申明,然后delete方式动态删除。

这里说一下查找内存情况,然后还让我查看电脑的内存有多大,win + r 然后输入dxdiag,这个是在Win 10中检查DirectX诊断工具版本,后续会附上DirectX的使用。除了通过DirectX诊断工具查看内存,也可以通过任务管理器了解内存,在任务栏右击,选择任务管理器,选中性能标签,就可以看到内存使用情况,总共有多少内存。还有可以通过右击电脑选择属性,选择“关于”,查看“设备规格”中的“自带RAM”。查出来内存有8G.

回到正题,内存有8G,但是我这边程序没有这么大,为啥受限呢。 所以想知道原因,主要类的局部变量,如果类定义申明,就是在栈空间,而这个栈空间是由操作系统控制的,根本没有用到所有的内存RAM的空间,下面说明了对于栈的空间,在Window下,栈的大小是2MB,Linux下,默认栈空间大小为8MB。而对于堆,比如32位系统最大不超过2G,而64位系统最大不超过4G,但相比较栈,这个堆的空间大多了。所以当需要分配一个非常大的内存时,请用new。所以这里将类动态申请,就将这个类需要很大空间放到堆那边了。

通过上面的梳理,可以知道内存的8个G是物理层面的概念,这个8个G可以进一步划分为操作系统和应用程序层面,就是栈和堆的概念。具体new使用方式和类定义申明方式的优缺点,可以参见下文。如果栈空间不够,编译不通过,或者运行溢出,指针乱飞。

说一个话外题,定义类用new方式定义,赋值给类的指针,配置100个颗粒了,编译通过了,但是运行指针飞了,比如访问数组超越边界,导致流程走到输出了selectnum的数据,我犯了一个小错误,认为selectnum之前都是正确的,其实这个前后都有问题,因为指针跑飞到这个selectnum, 是这个selectnum前面的代码导致的。所以想写这个贴子告知相关的编码的人员和我自己,不要犯这种定位问题的思路。所以我后来走读代码,发现测试代码半径数组设置为10,实际轮廓应该大于颗粒数据,因为有误判的轮廓,从而导致这个这个数组越界,导致指针跑飞,整个系统crash了。

一、new创建类对象与不new区别
new创建类对象需要指针接收

new创建类对象使用完需delete销毁

new创建对象直接使用堆空间,而局部不用new定义类对象则使用栈空间

new对象指针用途广泛,比如作为函数返回值、函数参数等

频繁调用场合并不适合new,就像new申请和释放内存一样

二、创建对象实例
1. 使用new创建对象

CTest* pTest = new CTest();

delete pTest;

pTest是用来接收类的对象指针。new申请的对象,则只有调用到delete时再会执行析构函数,如果程序退出而没有执行delete则会造成内存泄漏。

2. 不使用new,直接使用类定义声明:

CTest mTest;

一般作为局部变量或全局变量创建使用,此种创建方式,使用完后不需要手动释放,该类析构函数会自动执行,例如在一个函数内部作为局部变量创建使用,函数结束后系统会自动释放对应的栈空间不用手动delete。

#include <iostream>
using namespace std;


class A
{
private:
int n;
public:
A(int m):n(m)
{
}
~A(){}
};

int main()
{
A a(1); //栈中分配
A b = A(1); //栈中分配
A* c = new A(1); //堆中分配
  delete c;
return 0;
}
第一个隐式调用,第二个显式调用,两者都是在进程虚拟地址空间中的栈中分配内存,而第三种使用了new,在堆中分配了内存,栈中内存的分配和释放是由系统管理,堆中内存的分配和释放必须由程序员手动释放。

三、总结
一般来说对于一个进程栈的大小远远小于堆的大小,在Window下,栈的大小是2MB,Linux下,默认栈空间大小为8MB。对于堆,比如32位系统最大不超过2G,而64位系统最大不超过4G,当需要分配一个非常大的内存时,请用new。

栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。

堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在 堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就需要申请足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

由上可知,一般情况不使用new,直接使用类定义声明,申请内存特别大的情况下,或者作为函数返回值时,使用new创建对象。
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/u011555996/article/details/137423724

标签:定义,c++,内存,使用,空间,new,main,delete
From: https://www.cnblogs.com/happyhorse72/p/18245695

相关文章

  • python数据分析-笔记本内存和价格预测分析
    一、背景和研究意义计算机已成为现代社会不可或缺的工具,广泛应用于个人生活、学术研究和商业领域。随着科学技术的飞速发展,计算机不仅在性能上不断突破,在种类和品牌上也呈现出多样化和差异化。无论是办公、娱乐、学习还是创作,人们都离不开电脑的帮助。然而,随着电脑市场的不断......
  • [C++ Primer] 字符串、向量和数组
    [C++Primer]字符串、向量和数组标准库类型string标准库类型string表示可变长的字符序列,使用该类型需包含string头文件。作为标准库的i一部分,string定义在命名空间std中。拷贝初始化:使用等号(=)初始化一个变量直接初始化:不使用等号strings5="hiya"; //拷贝初始化s......
  • [C++ Primer] 变量和基本类型
    [C++Primer]变量和基本类型变量默认初始化如果定义变量时没有指定初值,则变量默认初始化,此时变量被赋予“默认值”。默认值到底是什么由变量类型决定,同时定义变量的位置也会对此有影响。内置类型:其默认值由定义的位置决定。定义于任何函数之外的变量被初始化为0。绝大多数......
  • 【C++面向对象】重载操作符
    C++将运算符重载扩展到自定义的数据类型,它可以让对象操作更美观。例如字符串string用加号(+)拼接、cout用两个左尖括号(<<)输出。运算符重载函数的语法:返回值operator运算符(参数列表);运算符重载函数的返回值类型要与运算符本身的含义一致。非成员函数版本的重载运算符函数:形......
  • 2024年华为OD机试真题-围棋的气-C++-OD统一考试(C卷D卷)
     2024年OD统一考试(D卷)完整题库:华为OD机试2024年最新题库(Python、JAVA、C++合集) 题目描述:围棋棋盘由纵横各19条线垂直相交组成,棋盘上一共19x19=361个交点,对弈双方一方执白棋,一方执黑棋,落子时只能将棋子置于交点上。“气”是围棋中很重要的一个概念,某个棋子有几口气,是指......
  • c++ 实现优先队列
    优先队列底层是堆,下面给出大根堆代码Push是在新节点到根节点的路径上寻找合适的插入位置;Pop是删除根节点之后维护大根堆,顺便为最后一个元素寻找合适的位置;1#include<bits/stdc++.h>23usingnamespacestd;45template<classT>6classp_queue{7pri......
  • 高性能版本的零内存分配LikeString函数(ZeroMemAllocLikeOperator)
    继上一篇文章在.NETCore,除了VB的LikeString,还有其它方法吗?(四种LikeString实现分享)分享了四种实现方式,笔者对这四种实现方式,不管是执行性能还是内存分配性能上,都不太满意。那么是否有好的实现方法呢?答案是有的。今天我们就搬出ReadOnlySpan<T>这个非常好用的结构类型,它是在.N......
  • c++在什么情况下会发生拷贝?
    在C++中,对象拷贝通常会在以下情况下发生:传递参数给函数:当你将对象作为参数传递给函数时,如果参数是按值传递的,那么会发生拷贝。例如:voidfunc(MyClassobj);//obj会被拷贝从函数返回对象:当函数返回一个对象时,如果函数返回的是对象本身而不是引用或指针,会发生拷贝。例......
  • C++中的流
    目录字节流(ByteStreams)字符流(CharacterStreams)主要区别在C++中,字节流和字符流是两种处理输入输出(I/O)的操作方式,它们都属于iostream库的一部分。它们的主要区别在于处理数据的基本单元和适用场景。字节流(ByteStreams)字节流以字节(byte)为基本处理单位,每个字节包含......
  • C/C++ 使用宏时应注意的问题总结
    使用C/C++宏时,为了确保代码的正确性、可读性和可维护性,现总结一些注意事项和最佳实践:1.定义常量使用#define定义常量时,要注意其类型不安全性。虽然它使用方便快捷,但缺乏类型检查可能导致问题。如果需要类型安全的常量,可以考虑使用const或constexpr。2.多重包含防范当宏......