c++动态内存管理与智能指针的相关知识点

目录
  • 引言
  • 一、介绍
  • 二、shared_ptr类
    • make_shared函数
    • shared_ptr的拷贝和引用
    • shared_ptr自动销毁所管理的对象…
    • 使用动态生存期的资源的类
    • 应用举例:Blob类
      • 定义Blob类
      • StrBlob的拷贝、赋值和销毁
  • 三、直接管理内存
    • 使用new分配内存
    • 使用new动态分配和初始化对象
      • 动态分配const对象
      • 内存耗尽
    • 使用delete释放内存
      • 基本介绍
    • 举例
  • 四、shared_ptr和new结合使用
    • new直接初始化share_ptr
    • 初始化时传入可调用对象代替delete
  • 五、unique_ptr
    • 基本操作
    • 传递和返回unique_ptr
    • 向unique_ptr传递删除器
  • 总结

引言

程序使用三种不同的内存

静态内存:static成员以及任何定义在函数之外的变量栈内存:一般局部变量堆内存(自由空间):动态分配的对象

静态内存和栈内存中的变量由编译器产生和销毁,动态分配的对象在我们不再使用它时要由程序员显式地销毁

一、介绍

动态分配内存

  • new():为对象分配空间,并返回指向该对象的指针
  • delete:销毁对象,并释放与之相关的内存

使用智能指针:定义在头文件memory中

  • shared_ptr:允许多个指针指向同一个对象
  • unique_ptr:“独占”所使用的对象
  • weak_ptr:伴随类,弱引用,指向shared_ptr所管理的对象

和容器一样,只能指针也是一种模板,需要给它传入一个参数来指定类型

二、shared_ptr类

声明shared_ptr:

shared_ptr<string> p1;	   //shared_ptr,可以指向string
shared_ptr<list<int>> p2;  //shared_ptr,可以指向list<int>

使用方式与普通指针一致,解引用返回它所指向的对象,在条件表达式中检查是否为空

//若p1不为空且指向一个空string
if(p1 && p1->empty()){
    *p1 = "hi";   //对p1重新赋值
}

make_shared函数

make_shared<typename>(arguments)

在动态内存中分配并初始化一个对象

返回指向此对象的shared_ptr指针

//指向一个值为42的int的shared_ptr
shared_ptr<int> p1 = make_shared<int>(42);
//指向一个值为"999"的string的shared_ptr
shared_ptr<string> p2 = make_shared<string>(3, '9');
//指向一个值为0的int的shared_ptr
shared_ptr<int> p3 = make_shared<int>();

没有传入参数时,进行值初始化

auto p4 = make_shared<string>(); //p4指向空string

shared_ptr的拷贝和引用

每个share_ptr都有一个关联的计数器

当拷贝shared_ptr时,计数器会递增

当shared_ptr被赋予新值或者shared_ptr被销毁(如一个局部的shared_ptr离开其作用域),计数器会递减

当一个shared_ptr的计数器==0时,内存会被释放

auto r = make_shared<int>(42);
r = q; //给r赋值,使它指向另一个地址
	   //递增q指向的对象的引用计数
	   //递减r指向的对象的引用计数
       //如果计数器为0,自动释放

shared_ptr自动销毁所管理的对象…

和其他类一样,shared_ptr类型也有析构函数

shared_ptr的析构函数会

  1. 递减指针所指向的对象的引用计数
  2. 当对象的引用计数为0时,销毁对象并释放内存…shared_ptr还会自动释放相关联对象的内存

举例:

//factory返回一个share_ptr,指向一个动态分配的对象
shared_ptr<Foo> factory(T arg){
    //对arg的操作
    return make_shared<Foo>(arg);
}

void ues_factory(T arg){
    shared_ptr<Foo> p = factory(arg);
    //使用p
}
//p离开了作用域,由于引用计数由1减到0,对象被销毁,内存释放

如果有其他引用计数也指向该对象,则对象内存不会被释放掉

//factory和上述一致
//ues_factory返回shared_ptr的拷贝
void use_factory(T arg){
    shared_ptr<Foo> p = factory(arg);
    //使用p
    return p; //返回p的拷贝,此时递增了计数器,引用数为2
}//p离开作用域,对象计数器引用2-1=1,对象内存没有释放

return shared_ptr时,如果不是返回引用类型,则会进行拷贝,shared_ptr的计数器+1后-1,最终shared的计数器不变

由于在最后一个shared _ptr销毁前内存都不会释放,保证shared_ptr在无用之后不再保留就非常重要了。如果你忘记了销毁程序不再需要的shared_ptr,程序仍会正确执行,但会浪费内存。

share_ptr 在无用之后仍然保留的一种可能情况是,你将shared _ptr存放在一个容器中,随后重排了容器,从而不再需要某些元素。在这种情况下,你应该确保用erase删除那些不再需要的shared_ptr元素。

如果你将shared ptr存放于一个容器中,而后不再需要全部元素,而只使用其中一部分,要记得用erase删除不再需要的那些元素。

使用动态生存期的资源的类

程序使用动态内存的三种原因

  • 程序不知道自己需要使用多少对象
  • 不知道所需对象的准确类型
  • 需要在多个对象间共享数据

容器类常出于第一种原因使用动态内存,在15章会看见出于第二种原因的例子,本节讨论第三种原因

先考虑这么一种情况:

我们要定义一个Blob类,当该类型的对象拷贝时,对象共享底层数据。

如b2 = b1时,b2,b1共享底层数据,对b2的操作也会印象到b1,且销毁b2时,b1的仍指向原数据

Blob<string> b1; //空Blob
{
    //新作用域
    Blob<string> b2 = {"a","an","the"};
    b1 = b2; //b1和b2共享数据
}//b2离开作用域,被销毁了,但b2的数据不能被销毁
//b1指向b2的原数据

应用举例:Blob类

定义Blob类

最终,我们希望将Blob定义为一个模板类,但现在我们先将其定义为StrBlob,即底层数据是vector<string>的Blob

class StrBlob{
public:
    //拷贝控制
    StrBlob();//默认构造函数
    StrBlob(initializer_list<string> il); //列表初始化
    StrBlob(const StrBlob& strb);
    //查询
    int size() const {return data->size();}
    bool empty() const {return data->empty();}
    //添加和删除元素
    void push_back(const string &t) {data->push_back(t);}
    void pop_back() {data->pop_back();}
    //访问元素
    string& front();
    string& back();
private:
    shared_ptr<vector<string>> data;
    //如果data[i]不合法,抛出异常
    void check(int i, const string &msg) const;
};

StrBlob的构造函数

StrBlob::StrBlob() : data(make_shared<vector<string>>())
        {cout<<"in StrBlob dafault"<<endl;};
StrBlob::StrBlob(initializer_list<string> il) :
    data(make_shared<vector<string>>(il))
    {cout<<"in StrBlob initializer_list"<<endl;}

元素访问成员函数

在访问时必须保证容器非空,定义check函数进行检查

void StrBlob::check(int i, const string& msg) const{
    if(i >= data->size())
        throw out_of_range(msg);
}

元素访问成员函数:

string& StrBlob::front(){
    //如果vector为空,check会抛出一个异常
    check(0, "front on empty StrBlob");
    return data->front();
}
string& StrBlob::back(){
    check(0, "back on empty StrBlob");
    return data->back();
}

StrBlob的拷贝、赋值和销毁

StrBlob使用默认的拷贝、赋值和析构函数对此类型的对象进行操作

当我们对StrBlob对象进行拷贝、赋值和销毁时,它的shared_ptr成员也会默认地进行拷贝、赋值和销毁

//由于data是private的
//在StrBlob中设置一个接口look_data
//look_data返回data的引用
class StrBlob{
public:
    //...
    shared_ptr<vector<string>>& look_data()
    {return data;} //返回引用,避免对象拷贝
private:
    //其余部分都不变
};

测试程序:

//测试程序
int main(){
    StrBlob b1;
    {//新作用域
        StrBlob b2 = {"first element","second element"};
        cout<<"before assignment : "
            <<b2.look_data().use_count()<<endl;
        b1 = b2;
        cout<<"after assignment  : "
            <<b2.look_data().use_count()<<endl;
    }//b2被销毁,计数器递减
    //b1仍指向b2的原数据
    cout<<b1.front()<<endl;
    //打印此时b1的计数器
    cout<<"b2 has been dstoryed : "
        <<b1.look_data().use_count()<<endl;
    return 0;
}

输出结果:

如果look_data用值返回,而不是引用返回,那么会存在拷贝【见6.2.2节笔记】,所有计数器的值会+1

三、直接管理内存

使用new分配内存

  • new分配动态内存
  • delete销毁动态内存

new和delete与智能指针不同,类对象的拷贝、赋值和销毁操作都不会默认地对动态分配的对象进行管理,无论是对象的创建还是销毁,都需要程序员显式地操作,在大型的应用场景中会十分复杂。

在熟悉C++拷贝控制之前,尽量只使用智能指针,而不是本节的方法管理动态内存

使用new动态分配和初始化对象

new type_name:返回一个指向该对象的指针

//pi指向一个动态分配,默认初始化的无名对象
int *pi = new int;
//*pi的值是未定义的
cout<<*pi<<endl;

对象是默认初始化这意味着:

  1. 指向的是:内置类型和组合类型对象。对象的值是未定义的
  2. 指向的是:类类型对象。调用默认构造函数

可以直接初始化动态分配的对象

  • 直接调用构造函数
  • 列表初始化
//pi指向对象的值为42
int *pi = new int(42);
//"9999999999"
string *ps = new string(10, '9');
//vector有5个元素,依次为0,1,2,3,4
vector<int> *pv = new vector<int>{0,1,2,3,4};

也可以值初始化

string *ps1 = new string(); //值初始化为空string
string *ps = new string;    //默认初始化为空string
int *pi1 = new int;         //默认初始化,值未定义
int *pi  = new int();       //值初始化,*pi = 0;

所以,初始化动态分配的对象是一个好习惯

动态分配const对象

用new可以分配const对象

和其他const对象一样,动态分配的const对象必须被初始化

//分配并初始化const int
const int *pi = new const int(1024);
//分配并默认初始化const string
const string *ps = new const string;

内存耗尽

如果new分配动态内存失败,返回一个空指针,并报出std::bad_alloc异常

int *p1 = new int; //返回空指针,抛出异常
int *p2 = new (nothrow) int; //如果分配失败,new返回空指针

我们第二种形式的new为定位new (placement new),其原因我们将在19.1.2节(第729页)中解释。

定位new表达式允许我们向new传递额外的参数。

在此例中,我们传递给它一个由标准库定义的名为nothrow的对象。如果将nothrow传递给new,我们的意图是告诉它不能抛出异常。如果这种形式的 new不能分配所需内存,它会返回一个空指针。bad_alloc和nothrow都定义在头文件new中。

使用delete释放内存

基本介绍

delete():接受一个指针,指向我们想要销毁的对象

执行两个操作

  • 销毁对象
  • 释放对应的内存

注意点:

  1. 保证只传给delete动态分配的指针,将一般指针传给delete,其行为是未定义的
  2. 同一块内存不能释放两次
  3. 不要忘记delete内存
  4. 不要使用已经delete的对象
int i, *pi = &i;
int *pd = new int();
delete pd; //正确:释放pd内存
pd = nullptr; //好习惯:指出pd不再指向动态内存
delete pi; //未定义:pi没有指向动态分配的内存
delete pd; //未定义:pd内存已经被释放

保证以上两点是程序员的责任,编译器并不会检查以上错误

举例

在被显式地delete前,用new动态分配的内存一直存在

Foo* factory(T arg){
    //处理arg
    return new Foo(arg);
}//调用者负责释放

void ues_factory(T arg){
    Foo *p = factory(arg);
    //使用p但不delete它
}//p离开了作用域,但它所指向的内存没有被释放!!

use_factory返回时,局部变量p被销毁。但此变量是一个内置指针,而不是一个智能指针,所以p所指向的内存并没有被销毁。

这样就产生了一块无名的内存块,存在又无法删除。

这也体现了智能指针与普通指针的区别:智能指针在离开自己的作用域,自己的变量名失效时,销毁指向的对象并释放关联内存;而new产生的指针不会。

修改use_factory:

void use_factory(T arg){
    Foo *p = factory(arg);
    //使用p
    delete p;  //记得释放p
}

坚持使用智能指针,可以避免上述的绝大部分问题

四、shared_ptr和new结合使用

new直接初始化share_ptr

可以用new返回的指针初始化share_ptr

该构造函数是explicit的

所以,不存在new产生的指针向shared_ptr的隐式类型转换,必须采用直接初始化,而不是拷贝初始化或者赋值

shared_ptr<int> p1(new int(42)); //正确:使用直接初始化
shared_ptr<int> p2 = new int(30);//错误:new产生的指针

同理,返回shared_ptr的函数不能返回new产生的指针

shared_ptr<int> clone(int p){
    //错误:构造函数为explicit,无法转换
    //return new int(p);
    //正确:显式地用int*构造shared_ptr<int>
    return shared_ptr<int>(new int(p));
}

如对隐式类型转换有疑问查看 7-5笔记第三点”隐式类类型转换”

初始化时传入可调用对象代替delete

默认情况下,一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete释放它所关联的对象。我们可以将智能指针绑定到一个指向其他类型的资源的指针上,但是为了这样做,必须提供自己的操作来替代 delete。我们将在12.1.4节介绍如何定义自己的释放操作。

五、unique_ptr

和shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象

基本操作

必须采用直接初始化

unique_ptr<double> p1;  //可以指向double的一个unique_ptr
unique_ptr<int> p2(new int(42)); //p2指向一个值为42的int

unique_ptr不支持拷贝与赋值

unique_ptr<string> p1(new string("hello"));
unique_ptr<string> p2(p1); //错误:不支持拷贝
unique_ptr<string> p3;
p3 = p1; //错误:不支持赋值

unique_ptr支持的操作

可以使用release和reset将指针的所有权从一个(非const)unique_ptr转移到另一个unique_ptr

//将所有权从p1,转移到p2
unique_ptr<string> p1(new string("hello"));
unique_ptr<string> p2(p1.release()); //release将p1置空
cout<<*p2<<endl; //输出 hello
unique_ptr<string> p3(new string("world"));
//p2绑定的对象被释放,p3置空,p2指向p3原来指向的对象
p2.reset(p3.release());
cout<<*p2<<endl; //输出: world

传递和返回unique_ptr

不能拷贝unique_ptr 的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr。最常见的例子是从函数返回一个unique_ptr:

unique_ptr<int> clone(int p){
    //正确:从int*创建一个unique_ptr<int>
    return unique_ptr<int>(new int(p));
}

还可以返回一个局部变量的拷贝

unique_ptr<int> clone(int p){
    unique_ptr<int> ret(new int(p));
    return ret;
}

对于两段代码,编译器都知道要返回的对象将要被销毁。在此情况下,编译器执行一种特殊的“拷贝”,我们将在13.6.2节(移动构造函数和移动运算符)中介绍它。

向unique_ptr传递删除器

//p指向一个类型为objT的对象
//并使用一个类型为delT的可调用对象释放objT
//p会使用一个名为fcnd的delT对象来删除objT
unique_ptr<objT, delT> p(new objT, fcn);

作为一个更具体的例子,我们将写一个连接程序,用unique_ptr来代替shared_ptr,如下所示:

void f(destination &d /*其他需要的参数*/)
{
	connection c = connect(&d);//打开链接
	unique_ptr<connection, decltype(end_connection)*>
		p(&c, end_connection);
	//使用链接
	//当f退出时(即使是由于异常而退出)
	//connection会调用end_connection正常退出
}

注意decltype(end_connection)返回一个函数类型,而函数类型不能作为参数,函数指针可以

所以要加上*表示函数指针
p(&c, end_connection)中,类似于数组名表示指针一样,函数名实际上就表示函数指针

所以也可写作p(&c, &end_connection),但没必要。【前一个&表示引用传递,后一个&表示取址得到指针】

总结

到此这篇关于c++动态内存管理与智能指针的文章就介绍到这了,更多相关c++动态内存管理与智能指针内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 一起来练习C++的指针

    目录 练习一:一级指针指向练习题 练习二:二级指针指向练习题 A选项: B选项: C选项: D选项: E选项: 总结 在C++中,const作用于指针时,可以看做是对指针权限的限制.这里我们先把指针的权限归为两种,分别为指向权限和修改权限.(ps:以上是为了理解方便,实际并没有如此规定) int a = 10, b = 20; int* p = &a; p = &b; // 改变指向的权限 *p = 30; // 修改内存的权限 const int* cp = &a; // 限制修

  • 一起来了解一下C++中的指针

    目录 1 指针的基本概念 2 指针变量的定义和使用 3 指针所占内存空间 4 空指针和野指针 5 const修饰指针 6 指针和数组 7 指针和函数 8 指针.数组.函数 总结 1 指针的基本概念 作用:可以通过指针间接访问内存. 内存编号是从0开始记录的,一般用十六进制数字表示.可以利用指针变量保存地址. 2 指针变量的定义和使用 指针变量定义语法: 数据类型* 变量名; &可以取地址;*可以取地址存放的值 示例: #include<iostream> using namespace

  • c++函数名指针和函数指针

    目录 前言 例 1 例 2 例 3 前言 我们先来看一下函数指针式如何定义的,假如我们有一个函数int fun(int){…};那么他对应的函数指针写法就应该是int (*p)(int);然后再对他进行赋值,即p=fun;之后你就可以在接下来的地方按p作为函数名来调用它用起来完全和fun一样.(注意这里的p指针并不是只能接受fun这个函数名,任何返回值是int,参数只有一个int的函数都可以把函数名赋给p) 首先说一下C/C++在创建一个变量的时候比如int a;相应的在内存就会分配一个4个字节

  • C++引用和指针的区别你知道吗

    目录 引用 1.引用概念 2.格式 3.引用特性 4.常引用 1.const引用 5.使用场景 1.引用作为参数 2. 引用作为做返回值 6.引用和指针的区别 7.引用和指针的不同点: 总结 引用 1.引用概念 引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间. 比如:李逵,在家称为"铁牛",江湖上人称"黑旋风 2.格式 类型& 引用变量名(对象名) = 引用实体: 例: void TestRe

  • C++的指针,引用和STL详解

    目录 指针.引用 指针 引用 STL STL中六大组件 常用容器用法介绍 vec.front(),vec.back()    返回vector的首尾元素 重载运算符 总结 对象的定义:对象是指一块能存储数据并具有某种类型的内存空间 一个对象a,它有值和地址:运行程序时,计算机会为该对象分配存储空间,来存储该对象的值,通过该对象的地址,来访问存储空间中的值. 指针.引用 指针 类型名 * 指针变量名: 每个变量都被存放在从某个内存地址(以字节为单位)开始的若干个字节中:"指针",也称作&

  • c++动态内存管理与智能指针的相关知识点

    目录 引言 一.介绍 二.shared_ptr类 make_shared函数 shared_ptr的拷贝和引用 shared_ptr自动销毁所管理的对象… 使用动态生存期的资源的类 应用举例:Blob类 定义Blob类 StrBlob的拷贝.赋值和销毁 三.直接管理内存 使用new分配内存 使用new动态分配和初始化对象 动态分配const对象 内存耗尽 使用delete释放内存 基本介绍 举例 四.shared_ptr和new结合使用 new直接初始化share_ptr 初始化时传入可调用对象

  • C语言的动态内存管理你了解吗

    目录 C/C++内存分配方式 C++内存管理方式 new和delete的使用 new和delete的骚操作 new和delete的区别 重载new和delete 定位new表达式 内存泄露 总结 C/C++内存分配方式 在学习C语言阶段的时候,创建一个变量,编译器会为它分配一块内存.而创建一个C++对象的时候,编译器会为这个对象分配内存,并且调用合适的构造函数进行初始化. 那么编译器的内存分配方式是怎样的呢? 内存分配可以有以下的几种方式 从静态存储区分配.这样的分配方式在程序开始前就可以为对象

  • 深入理解C++中的new/delete和malloc/free动态内存管理及区别介绍

    malloc/free和new/delete的区别 malloc/free是C/C++标准库的函数:new/delete是C++操作符. malloc/free只是动态分配内存空间/释放空间:new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理资源. malloc/free需要手动计算类型大小且返回值类型为void*:new/delete可自动计算类型的大小,返回对应类型的指针. malloc/free管理内存失败会返回0:new/delete等的方式管理内存失败会抛出异常

  • C++动态内存管理详解

    目录 1.C/C++程序地址空间 2.C语言动态内存管理 (1)malloc (2)calloc (3)realloc (4)free 3.C++动态内存管理 (1)C++为什么要设计一套自己专属的动态内存管理方式? (2)new/delete定义 1)new/delete操作内置类型 2)new/delete操作自定义类型 (3)new/delete的实现原理 4.malloc/free和new/delete的区别 共同点: 不同点: 5.内存泄漏 总结 1.C/C++程序地址空间 计算机物理

  • C语言动态内存管理的实现

    目录 1. 摘要 2. 为什么存在动态内存管理 3. 动态内存函数 3.1 malloc 3.2 free 3.3 calloc 3.4 realloc 4. 常见的动态内存错误 5. 几个经典笔试题 参考答案 6. 参考文献 1. 摘要 本文主要详解C语言中的动态内存分配 2. 为什么存在动态内存管理 我们先来看一段变量的声明: double x = 1.000000; char str[] = "abcdef"; 好的,上述变量的声明有何特点呢? 请思考一下,我的朋友. 对,没错,

  • C语言动态内存管理分析总结

    目录 什么是动态内存分配 动态内存函数的介绍 free malloc calloc realloc 动态内存管理中常见的错误 对NULL指针的解引用操作 对动态开辟空间的越界访问 对非动态开辟内存使用free释放 使用free释放一块动态开辟内存的一部分 对同一块动态内存多次释放 动态开辟内存忘记释放(内存泄漏) 一些经典的笔试题 题目1 题目2 题目3 题目4 柔性数组 柔性数组的特点 柔性数组的优势 什么是动态内存分配 我们都知道在C语言中,定义变量的时候,系统就会为这个变量分配内存空间,而

  • C语言动态内存管理介绍

    目录 前言: C 语言为内存的分配和管理提供了几个函数: 1.malloc() 用法 2.calloc() 用法 3.realloc() 与 free() 用法 前言: 简单记录一下,内存管理函数 为什么使用动态内存呢? 简单理解就是可以最大限度调用内存 用多少生成多少,不用时就释放而静止内存不能释放 动态可避免运行大程序导致内存溢出 C 语言为内存的分配和管理提供了几个函数: 头文件:<stdlib.h> 注意:void * 类型表示未确定类型的指针  1.malloc() 用法  分配一块

  • 关于C语言动态内存管理介绍

    目录 1.为什么需要动态内存分配 2.有关动态内存函数介绍 2.1 malloc和free 2.2 calloc函数 2.3 realloc函数 3. 常见的动态内存错误 3.1 对NULL指针进行解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 使用free释放一块动态开辟内存的一部分 3.5 对同一块动态内存多次释放 3.6 动态开辟内存忘记释放(内存泄漏) 总结 1.为什么需要动态内存分配 关于这个问题,我们先看看我们之前是如何开辟内存的. i

  • C语言 动态内存管理全面解析

    目录 1. 为什么存在动态内存分配 2. 动态内存函数的介绍 2.1 malloc和free 2.2 calloc 2.3 realloc 3. 常见的动态内存错误 3.1 对NULL指针的解引用操作 3.2 对动态开辟空间的越界访问 3.3 对非动态开辟内存使用free释放 3.4 使用free释放一块动态开辟内存的一部分 3.5 对同一块动态内存多次释放 1. 为什么存在动态内存分配 *动态内存开辟在堆区* 我们已经掌握的开辟内存方式是类型直接定义变量,开辟的内存是固定的,像: int a=

  • c++动态内存管理详解(new/delete)

    目录 前言 用法上 对内置类型 对自定义类型 new/delete底层原理 重载类的专属operatornew和operatordelete 定位new new/delete与malloc/free区别总结 内存泄漏 总结 前言 想必大家对c语言的动态内存分配并不陌生,忘了的小伙伴也可以看看我的这篇文章C语言动态内存分配 c语言的动态内存分配由于有些地方用起来比较麻烦同时检查错误的机制不适合c++,因此c++引入new/delete操作符进行内存管理,下面我们来深入探讨c++为什么要引入new/

随机推荐