【C++】类动态内存分配

 

目录

1.回顾 malloc / free

1.1内存 Memory

1.2程序内存模型

1.3动态内存分配

2.面向对象动态内存分配: new / delete

2.1C++的动态内存分配

2.2new 和 delete 运算符

2.2.1new / delete:

2.2.2new[] 和 delete[] 运算符

3.两者异同

4.虚析构函数

4.1内存泄漏 VS 内存溢出

4.2虚析构函数

5.例题


1.回顾 malloc / free

1.1内存 Memory

计算机中一切正在运行的程序都运行在内存中
程序(代码、数据)都加载在内存中

1.2程序内存模型

栈:变量、对象、调用函数等所需要的空间由编译器管理,
来一个就生成一个压入栈中,结束一个就弹出一个。
堆:由程序员自己编写代码管理: 申请 ,使用, 释放 。( 动态内存分配)
编译器不维护。

1.3动态内存分配

动态:
需要时申请
用完后归还
分配:
告诉操作系统你要多大的空间(字节数)
申请成功给你一个地址(指针)
C语言:malloc( m emory alloc ate ) / free
• malloc申请堆区内存:
int* ptr = (int*)malloc(sizeof(int) * 3); // 获得指定字节数的堆区内存
*(ptr + 2) = 10; // 使用这块内存
• free释放占用的内存:
free(ptr);
如果不释放内存空间 => 内存泄露 => 导致内存不够 => 内存溢出

2.面向对象动态内存分配: new / delete

2.1C++的动态内存分配

思考:C++相比C语言主要多了什么?面向对象。
进一步思考:定义一个对象和定义一个普通变量有何区别?
普通变量:分配足够空间即可存放数据
对象:除了需要空间,还要 构造 / 析构
类比:
malloc / free: 租用一个仓库 -> 存放货物 -> 货物卖完 -> 退租走人
new / delete : 租一块地 -> 修建大厦 -> 到期不用了 -> 爆破拆除善后 -> 地归原主

2.2new 和 delete 运算符

2.2.1new / delete:

申请所需大小的内存 -> 构造对象 -> 返回指针供使用 -> 析构对象 -> 释放空间
#include<iostrem>
using namespace std;
class A {
public:
    int n;
    A() { cout << "Ctr1" << endl; }
    A(int n) : n(n) { cout << "Ctr2" << endl; }
    void func() { cout << "func called" << endl; }
    ~A() { cout << "Dtr" << endl; }
};
int main()
{
    A* p1 = new A;
    p1->func();
    delete p1;
    A* p2 = new A(10);
    p2->func();
    delete p2;
    return 0;
}

打印结果
Ctr1
func called
Dtr
Ctr2
func called
Dtr

2.2.2new[] 和 delete[] 运算符

new[]:申请空间并批量构造对象数组,返回 首元素指针
delete[]:批量析构对象数组并释放空间,只能用于new[]出来的指针
class A {
public:
    A() { cout << "A constructed\n"; }
    void func() { cout << "func called\n"; }
    ~A() { cout << "A destructed\n"; }
};
// ... A* p_a = new A[6];
(p_a + 1)->func();
delete[] p_a;
A constructed
A constructed
A constructed
A constructed
A constructed
A constructed
func called
A destructed
A destructed
A destructed
A destructed
A destructed
A destructed

3.两者异同

malloc / free VS new / delete
都用于动态内存分配(申请 - 使用 - 释放)
都发生在堆区内存上
申请成功得到的都是指针,通过指针操作得到的内存空间/对象
malloc / free 是一对函数,new / delete 是一对 运算符
malloc / free 仅申请空间(不关心空间用途),而 new / delete 还会构造/析构对象
malloc / free 得到 void* 指针,而 new / delete 得到指向指定类型的指针
new / delete 底层有使用 malloc / free

4.虚析构函数

4.1内存泄漏 VS 内存溢出

内存 溢出
一辆车只能坐5个人,来第6个人没法坐了, 内存空间不够用了
内存 泄漏
公司一共有10辆车,本来最多能坐50人
小明把其中一辆车 借走了一直不还 ,导致别人都 用不了 这辆车
本来能坐50人,现在来46个人就溢出了, 增大了内存溢出可能

4.2虚析构函数

思考:下面代码段有什么问题?
class A {
public:
    char* pA;
    A()
    {
        pA = new char[100];
    }
    virtual void func()
    {
        cout << "func in A"<<endl;
    }
    ~A()
    {
        delete[] pA;
    }
};
class B : public A
{
    public:
    char* pB;
    B()
    {
        pB = new char[100];
    }
    void func()
    {
        cout << "func in B"<<endl;
    }
    ~B()
    {
        delete[] pB;
    }
};
A* ptr = (A*) new B;
ptr->func();
delete ptr;
父类指针指向子类对象,释放指针时只会释放指针类型对象,其实子类对象没有被释放
ptr的类型是A*
因此~B()没被调用
导致内存泄露啦!
请务必把父类的析构函数设置为虚函数!
上面代码解决方案如下(在父类析构函数前加上virtual)
class A {
public:
    char* pA;
    A()
    {
        pA = new char[100];
    }
    virtual void func()
    {
        cout << "func in A"<<endl;
    }
    virtual~A()
    {
        delete[] pA;
    }
};
class B : public A
{
    public:
    char* pB;
    B()
    {
        pB = new char[100];
    }
    void func()
    {
        cout << "func in B"<<endl;
    }
    ~B()
    {
        delete[] pB;
    }
};
A* ptr = (A*) new B;
ptr->func();
delete ptr;

5.例题

Key 1 :new/delete 是一对运算符,动态创建/销毁单个对象(或基础数据)
Key 2 :new[]/delete[] 动态创建/销毁对象数组(或基础类型数组)
Key 3 :new/delete和malloc/free的区别在于前者会构造/析构对象
Key 4 :内存泄露(借了不还):导致内存溢出(资源不够)的可能性增大
Key 5 :虚析构函数是为了避免子类资源泄露(如内存泄露或文件句柄不释放)
1.下面有关构造函数和new运算符关系正确的说法是:D
A.new运算符只能用于类,因为基础数据类型没有构造函数
B. 构造函数一定调用new运算符
C.要创建新类的实例时,需要先使用new运算符,然后调用构造函数进行初始化
D. 使用new运算符动态产生类的对象时,new运算符会自动调用构造函数
2.关于delete和delete[]运算符的下列描述中,错误的是:C
A. 它们不能用于野指针和垂悬指针
B. 它们可以用于空指针
C. 对一个指针可以使用多次该运算符
D. 使用delete[]无需在括号符内写待删数组的维数
解析:
野指针:不知道指向什么乱七八糟地址
垂悬指针:指向的地址已经被回收/清理了
空指针:nullptr关键字,表示“没有地址”的含义
3.小明做项目时定义了一些类:F←S←G。项目运行时,他发现系统存在内存泄露和文件句柄占用的现象,下面
哪个举措有可能解决该问题:
A. 将成员函数全部声明为内联函数
B. 避免使用重载函数
C. 将G的析构函数声明为虚析构函数
D. 将F的析构函数使用virtual关键字声明
4. 填空:下列代码有可能导致_内存泄露_ ,增大 __内存溢出__的可能性。
class Base
{
private:
    char* pc;
    public:
    Base(int n) : pc(new char[n]) {}
    ~Base() { delete[] pc; }
};
class Derived : public Base
{
private:
    int* pi;
    public:
    Derived(int n1, int n2) : Base(n1) { pi = new int[n2]; }
    ~Derived() { delete[] pi;}
};
5.请补全下列代码,避免内存泄漏问题
class A {
private:
    int n;
    public:
    A(int n) : n(n) {}
    _______________ {}
};
class B : public A {
private:
    int* data;
public:
    B(int n) : A(n) { data = new int[n]; }
    ~B()
    {
    _____________________;
    }
};
int main () {
A* p = new B(100);
// 省略对p的操作
delete p;
}
答案
virtual ~A()
delete[] data;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值