c++动态内存管理/模板

一,动态内存管理

(1)C语言和c++区别

在C语言中,我们常用malloc和calloc申请空间,用free来释放空间,而现在我们更多的情况下用new来申请空间,用delete来释放空间。

申请一个空间:

int* p2 = (int*)malloc(sizeof(int));//空间值为随机值
int* p3 = new int;//空间值为随机值

申请多个空间:

int* p4 = (int*)malloc(sizeof(int) * 10);
int* p5 = new int[10];

释放一个空间:

free(p2);

delete p3;

释放多个空间:

free(p4);

delete[ ] p5;

(2)申请空间的同时进行初始化:

int* p6 = new int(10);申请4个字节,一个空间,用()进行初始化
int* p7 = new int[10] {1, 2};申请多个空间,[ ]里面写申请空间的个数,{ }中,写初始化的值
int* p8 = new int[10] {};//默认为0,当写成这个样子的时候,默认是初始化为零,如果没有写{}则初始化为随机值(不同的编译器也有可能不同)

为什么出++中改用new来申请空间,而不用malloc?

答:因为malloc没有办法很好的支持动态申请的自定义对象初始化。

例如,用new来开辟自定义类型的空间并初始化

class A {
private:  int _a;
public:
    A(int a = 0)
        :_a(a)
    {}
}

A* P2 = new A;
A* P3 = new A(3);

delete P2;
delete P3;

A* P4 = new A[10];
delete[ ] P4;

将A类实例化为aa1,aa2,并开辟一块空间,将这两个对象放到里面:

A aa1(1);
A aa2(4);
A* P5 = new A[10]{ aa1,aa2 };
delete[] P5;

以上式子可以简写为以下代码:

A* P6 = new A[10]{ A(1),A(5) };
delete[] P6;

继续将其简化(运用:隐式类型转换):

A* P7 = new A[10]{ 1,5 };
delete[] P7;

——————————————————————————————

(3)new和delete

用stack类,来深入探究一下new和delete的用法:

class Stack {
private:
    int* _arr;
    int _top;
    int _capacity;
public:
    Stack(int capacity=4) {
        int* _arr = new int[capacity];
        _top = 0;
        _capacity = capacity;
    }
    ~Stack() {
        delete _arr;

    }
    void push(int val) {
        _arr[_top] = val;
        _top++;
    }
};

实例化一个对象,并将对象初始化为n

int n; cin >> n;
Stack* pst = new Stack(n);

由此可知:这里在栈上开辟了对象的空间,并调用了构造函数(开辟了数组的空间,也就是在堆上开辟了空间)

delete pst;

由此可知:调用了析构函数,释放了堆上的空间(数组的空间),并释放了栈上对象的空间

(4)operator new和operator delete

operator new的用法:

operator new的用法与malloc的用法相似,这时这里开辟空间失败时,不会返回null,而是直接抛异常
例如:Stack* pst = (Stack*)operator new (sizeof(Stack));

在执行这条代码时:int* arr = new int[10];会调用operator new[ ],它又会调用operator new。

注意:当申请一块空间时,用的是new ,与之相对的是delete,但对于多块空间来说,用的是new[ ],而与之相对的是delete [ ],在使用时要匹配的使用,不同的编译器对这两种方式有不同的处理方式,如果随意匹对使用,会在意想不到的地方冒出bug。

总结:要匹配使用

(5)new和operator new ,delete和operator delete联系

new和operator new 区别:new里面包含了operator new和构造函数

delete和operator delete:delete里面包含了operator delete和析构函数

一般来说:我们通常使用new和delete,operator new和operator delete一般是由编译器来使用的(我们使用new时,编译器再调用operator new)但是如果我们就是想用operator new(也就是说:想将开辟空间和实例化分开来进行)也可以由以下代码实现:

开辟空间:Stack* pst1 = (Stack*)operator new (sizeof(Stack));
调用构造函数:new(pst1)Stack(4);(这里用到了:显示调用构造函数)

注意:不可以写成pst1->Stack(4);来调用构造函数。但可以pst1->~Stack()来调用析构,operator delete(pst1);

pst1->~Stack();
operator delete(pst1);//这两步相当于直接调用delete

(6)总结:malloc和new,free和delete区别

1,malloc和free是函数,new和delete是操作符
2,malloc申请的空间不会初始化,new可以初始化
3,malloc需要手动计算空间的大小,new不需要
4,malloc申请失败会返回null,new抛异常
5,malloc的返回值是void*,所以必须要强转
6,new会调用构造函数,delete会调用析构函数 

二,内存区间

·1,代码区(常量区):存放程序代码;

·2,全局变量与静态变量区(数据段):存放全局变量和静态变量以及对象;

·3,栈区(局部变量区):存放局部变量;

·4,堆区(自由存储区):存放动态分配对象。

动态:分配内存(运行时) 存储(堆区) 释放内存(手动释放)

静态:分配内存(编译时) 存储(栈区) 释放内存(自动释放)

小试牛刀:

int gg = 1;
static int ss = 1;
void test() {
    static int aa = 1;
    int bb = 1;
    int num[10] = { 1,2,3,4 };
    char char2[] = "abcd";
    const char* pchar3 = "abcd";
    int* p1 = (int*)malloc(sizeof(int) * 4);
    free(p1);
}

————————————————————————————

gg 为全局变量,储存在静态区
ss 静态变量,在静态区
void test() {
    aa静态变量,在静态区
    bb为局部变量,储存在栈
    num为局部变量,储存在栈
    char2为局部变量,储存在栈(将常量区的abcd\0,拷贝到char2数组中)
    *char2;//栈(数组名代表首元素地址,解引用,指向首元素)
    const char* pchar3 = "abcd";//栈(变量为指针,存放地址,这个地址指向常量区的字符串)这个地址存放在栈上
    *pchar3;//常量区
     p1 //栈(指针)
    *p1;//堆(在堆上开辟空间)
}

如果堆上的空间不手动进行释放,会引起内存泄漏,内存泄漏在长期运行的程序上危害极大

三,模板:

分为函数模版和类模板,模版的参数是一个泛型,也就是不同类型的参数可以调用同一个模版来实现功能,极大的简化了代码,和人工成本。编译器通过参数的不同,用模板实例化生成对应的函数或者类。

写法:template<typename/class T>放在函数或者类的前面

模板的参数可以有多个,例如:template<typename T1, typename T2>

函数模板:

template<typename T>
void Swap(T& a,T& b) {
    T tmp = a;
    a = b;
    b = tmp;
}

类模板:

template<class T>
class Stack2 {
private:
    T* _arr;
    int _top;
    int _capacity;
public:
    Stack2(int capacity = 4) {
         _arr = new T[capacity];
        _top = 0;
        _capacity = capacity;
    }
    ~Stack2();
    void push(const T& val) {
        _arr[_top] = val;
        _top++;
    }
};

实例化的时候写法为,类型+引用,这里的类型不是类名,是类名<T>:

Stack2<int > s1;
Stack2<double > s1;

当模板只有一个参数,但参数确实不同的类型时,可以进行一下方法进行处理:

template<typename T >
T Add(const T& a, const T& b) {
    return a + b;
}

cout << Add((int)2.8, 3) << endl;
cout << Add(2.8, (double )3) << endl;
cout << Add<int >(2.8, 3) << endl;
cout << Add<double >(2.8, 3) << endl;

多参数的模板:

template<class T,int n>
class Num {
private:
    T arr[n];
public:
    Num(T s[]) {
        for (size_t i = 0; i < n; i++)
        {
            arr[i] = s[i];
        }
    }
};
    int arr[7] = { 1,2,3,4,5,6,7 };
    Num<int,7> num1(arr);//这样进行初始化
    

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值