栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

c++ 动态内存

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

c++ 动态内存

文章目录
  • 前言
  • 动态内存与智能指针
    • shared_ptr类
    • 直接内存管理
    • shared_ptr和new结合使用
    • 智能指针和异常
    • unique_ptr
    • weak_ptr
  • 动态数组
    • new和数组
    • allocator类
  • 使用标准库:文本查询程序
    • 特别辣的鸡
    • 比较优秀的代码
  • 总结


前言

  全局对象在程序启动时分配,在程序结束时销毁。局部自动对象:当进入其定义所在的程序块是被创建,在离开块时销毁。局部static对象在第一次使用前分配,在程序结束时销毁。

  除自动和static对象外,C++还支持动态分配对象。动态分配对象的生存期与他们在哪里创建无关,只有当显式地被释放时,这些对象才会被销毁。

  为了更安全的使用动态对象,标准库定义了两个智能指针类型来管理动态分配的对象。

  静态内存:保存局部static对象、类static数据成员以及定义在任何函数之外的变量
  栈内存 : 保存定义在函数内的非static对象
二者由编译器自动创建或销毁
static对象:在使用之前分配,在程序结束时销毁
栈 对 象 :仅在其定义的程序块运行时才存在

除了静态内存和栈内存,每个程序还拥有一个内存池。称为自由空间(free store)或堆(help),用于存储动态分配(dynamically allocate)的对象(在程序运行时分配的对象,生存期由程序控制,不使用时必须显式地销毁)
正确的管理动态内存非常棘手


动态内存与智能指针

智能指针也是模板,定义在头文件
智能指针有点像Python中的对象,有引用计数器,自动释放内存等

关键字:new delete 智能指针: shared_ptr、unique_ptr、weak_ptr

shared_ptr类
shared_ptr p1;    //shared_ptr, point to string
shared_ptr> p2; //shared_ptr, point to lsit

shared_ptr和unique_ptr都支持的操作

shared_ptr sp空指针
unique_ptr up空指针
pp为空 false,p不为空 true
*p解引用
p -> mem
p.get()返回p中保存的指针。
若智能指针释放了其对象,返回的指针所指的对象也就消失了
swap(p, q)交换p和q中的指针
p.swap(q)交换p和q中的指针

shared_ptr独有的操作

make_shared (args)返回一个shared_ptr,指向一个动态分配的类型为T的对象
使用args初始化此对象
shared_ptr p(q)p式shared_ptr q的拷贝;此操作会递增q中的计数器。
q中的指针必须能转换为 T*
p = qp q都是shared_ptr,所保存的指针必须能相互转换。此操作会递减p的引用计数,
递增q的引用技术;若p的引用计数变为0,则将其管理的原内存释放
p.unique()若p.use_count() 为1,返回true;否则返回false
p.use_count()返回与p共享对象的智能指针数量;可能很慢,主要用于调试
shared_ptr p3 = make_shared(42);        //p3指向 42
shared_ptr p4 = make_shared(4, '9'); //p4指向 string("9999")
shared_ptr p5 = make_shared();			 //p5指向 int 初始化0
//p6指向一个动态分配的空vector
auto p6       = make_shared>();

shared_ptr的拷贝和赋值
引用计数(reference count)

auto p = make_shared(42); //p指向的对象只有p一个引用者
auto q(p);                     //p和q指向相同对象,此对象有两个引用者
auto r = make_shared(42); //r指向的int只有一个引用者
r = q;                         //给r赋值,令他指向另一个地址
                               //递增q指向的对象的引用计数器,递减r原来指向的对象的引用计数器
                               //r原来指向的对象已没有引用者,会自动释放


**shared_ptr 自动销毁所管理的对象······ **
析构函数destructor:与构造函数相反,与对象结束生命周期有关

······shared_ptr 还会自动释放相关联的内存

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

  1. 程序不知道自己需要多少对象
  2. 程序不知道所需对象的准确类型
  3. 程序需要在多个对象间共享数据
    定义StrBlob类
class StrBlob{
public:
	using size_type = std::vector::size_type;
	StrBlob();
	StrBlob(std::initializer_list il);
	size_type size() const {return this->data->size();}
	bool empty() const {return this->data->empty();}
	//添加和删除元素
	void push_back(const std::string &t){ data->push_back(t);}
	void pop_back();
	//元素访问
	std::string& front();
	std::string& back();
private:
	std::shared_ptr> data;
	//如果data[i]不合法,抛出一个异常
	void check(size_type i, const std::string &msg) const;
};

StrBlob::StrBlob(): data(make_shared>() ) {}
StrBlob::StrBlob(std::initializer_list il):
	data( make_shared>(il) ) {}

void StrBlob::check(size_type 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, "front on empty StrBlob");
	return data->back();
} 
void StrBlob::pop_back(){
	check(0, "front on empty StrBlob");
	data->pop_back();
}

直接内存管理

new分配内存,delete释放new分配的内存

int *pi = new int; //pi指向一个动态分配的、未初始化的无名对象
				   //内置类型或组合类型的对象的值将是未定义的,类类型对象将用默认构造函数进行初始化
string *ps = new string; //初始化为空string
int *pi = new int(1024);   //pi指向的对象的值为1024
string *ps = new string(3, '9'); /}//ptr离开作用域,被销毁

int* x(new int(1024));       //危险!x是一个普通指针不是一个智能指针
process(x); 			     //❌
process(shared_ptr(x)); //合法,但内存会被释放!
int j = *x;                  //未定义:x是一个空悬指针!它指向的内存已经被释放

······不要使用get初始化另一个智能指针,或为智能指针赋值

{
shared_ptr p(new int(42)); //引用计数为1
//.get()返回内置指针,指向p管理的对象
int *q = p.get();               //正确,但使用q是要注意,不要让他管理的指针被释放,不能delete
{
//未定义:两个独立的shared_ptr指向相同的内存
	shared_ptr(q);
}//程序块结束,q被销毁,它指向的内存被释放
int foo = *p;   //未定义:p指向的内存已经被释放了
}//程序块结束 p 所指向的内存会被第二次delete
智能指针和异常
void end_connection(connection *p) {disconnect (*p)};

void f(T &d ){
	T c = connect(&d);
	shared_ptrp(&c, end_connection);
}//当f退出时(即使时由于异常而推出),connection会被正确关闭

基本规范:

  • 不使用相同的内置指针初始化(或reset)多个智能指针
  • 不delete get()返回的指针
  • 不使用get() 初始化或reset另一个智能指针
  • 如果使用get()返回的指针,当最后一个对应的智能指针销毁后,指针就变为无效了
  • 如果使用智能指针管理的资源不是new分配的内存,传递给它一个删除器
unique_ptr

unique_ptr p1(new string("abc"));
unique_ptr p2(p1.release()); //将所有权从p1转移给p2,release将p1置为空
unique_ptr p3(new string("Tex"));
p2.reset(p3.release()); //将所有权从p3转移给p2,reset释放了p2原来指向的内存

p2.release();          //❌ p2不会释放内存,而且我们丢失了指针
auto p = p2.release(); //✔  但我们必须计的delete p

向unique_ptr传递删除器

void f(destination &d ){
	connection c = connect(&d);
	unique_ptr p(&c , end_connection);
	//使用连接
}//当f推出(即使是异常退出),connection会被正确关闭
const int& anotherfunc(const int &a){
    return a;
}
int main(){
    decltype(anotherfunc)* p = anotherfunc;
    const int&(*pfunc)(const int&)=0;
    pfunc = anotherfunc;
    cout << (pfunc == x) << endl;  
return 0;}
weak_ptr

auto p = make_shared(42);
weak_ptr wp(p); 			//wp弱共享p;p的引用计数器未改变
if (auto np = wp.lock()){ }
动态数组

两种一次分配一个对象数组的方法

new和数组
int *p = new int[get_size()]; //p为指向数组元素类型的指针,而非指向数组的指针
                              //未初始化
int *p = new int[10]();       //初始化10个0
string *ps1 = new string[10]; //10个空string
string*ps2 =new string[9](9); //9个空string
int *p = new int[10]{1,2,3,4};
string *ps=new string[10]{"a", string(3,'x')};                              

若,初始化器的数目大于元素数目,则new表达式失败,抛出pad_array_new_length异常,定义在new头文件中

char arr[0];            //❌不能定义长度为0的数组
char *cp = new char[0]; //✔但cp不能解引用

释放动态数组

delete p;    //p必须指向一个动态分配的对象或为空,否则是未定义行为
delete [] p; //p必须指向一个动态分配的数组或为空,否则是未定义行为

智能指针和动态数组

//up指向一个包含10个未初始化int的数组
unique_ptr up(new int[10]);
up.release();                      //自动调用delete[]销毁其指针
for(size_t i=0; i!= 10; ++i)
	up[i] = i;


shared_ptr不直接支持管理动态数组
不要用动态数组,用vector等容器

//为了使用shared_ptr, 必须提供一个删除器
shared_ptr sp(new int[10], [](int *p){delete[]p;})
sp.reset(); //使用我们提供的lambda释放数组,它使用delete[]
			//否则,shared_ptr默认使用delete,就会碰到delete 动态数组指针的未定义行为
//shared_ptr未定义下标运算符,并且不支持指针的算术运算
for (size_t i=0; i!=10; ++i)
	*(sp.get() + i) = i;    //使用get获取一个内置指针
allocator类

allocator类定义在头文件memory中

allocator alloc;
auto const p = alloc.allocatr(n);
auto x = p;
alloc.construct(x++);			//*x为空字符串
alloc.construct(x++, 10, 'c');  //*x为 cccccccccc
alloc.construct(x++, "hi");		//*x为 hi

cout << *p << endl;   			//正确
cout << *x << endl;				//严重错误,x指向为构造的内存

while(x != p)
	alloc.destroy(--x) ;		//释放我们真正构造的string,析构

alloc.deallocate(p, n);			//释放内存

拷贝和填充未初始化内存的算法

//vector vi
auto p = alloc.allocate(vi.size()*2);
auto x = uninitialized_copy(vi.begin(), vi.end() ,p);
uninitialized_fill_n(x, vi.size(), 42)
//alloc总共n = vi.size()*2个元素,前n个拷贝,后n个赋值42
使用标准库:文本查询程序 特别辣的鸡
class TextQuery{
    using inner_t1 = map>>;
private:
    inner_t1  wd2lineMap;
    vector  text;
    ifstream&     infile;
    void  Map_function();

public:
    TextQuery(ifstream &infile):
        infile(infile), wd2lineMap(), text() {Map_function();};
    string query(const string&);

};
void TextQuery::Map_function(){
    if(infile){
        string line;
        int lineNum=-1;
        while ( getline(infile, line) ){
            ++lineNum;
            text.push_back(line);
            istringstream wds(line);
            string wd;
            while(wds >> wd){
                auto res = wd2lineMap.find(wd);
                if (res == wd2lineMap.end()){
                    auto paired = pair>(1, {lineNum});
                    wd2lineMap.insert({wd, paired});
                }else
                    ++(wd2lineMap[wd].first);
                    wd2lineMap[wd].second.insert(lineNum);
            }
        }
    }
}
string TextQuery::query(const string&s){
    auto iter = wd2lineMap.find(s);
    if (iter == wd2lineMap.end()){
        return string("not found!n");
    }else{
        string res;
        auto paired = wd2lineMap[s];
        cout << "word: "<< s << " occures "<1?" times":" time") << endl;
        for (auto c:paired.second)
            if (c>=0 && c
    TextQuery tq(infile);
    while(true){
        cout << "enter word to look for, or q to quit:n";
        string s;
        if (!(cin >> s) || s == "q") break;
        // cout << tq.query(s);
        print(cout , tq.query(s)) << endl;
    }
}
int main(){ 
    ifstream infile("./text.txt");
    runQueries(infile); 
return 0;}
比较优秀的代码
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

class QueryResult{
friend std::ostream& print(std::ostream&, const QueryResult&);
public:
    using line_no = std::vector::size_type;
    QueryResult(std::string s,
                std::shared_ptr> p,
                std::shared_ptr> f):
        sought(s), lines(p), file(f) {}
private:
    std::string sought;
    std::shared_ptr> lines;
    std::shared_ptr> file;
};
class TextQuery{
public:
    using line_no = std::vector::size_type;
    TextQuery(std::ifstream&);
    QueryResult query(const std::string&) const;
private:
    std::shared_ptr> file;
    std::map>> wm;
};

TextQuery::TextQuery(ifstream &is):file(new vector){
    string text;
    while (getline(is, text)){
        file->push_back(text);
        int n = file->size() - 1;
        istringstream line(text);
        string word;
        while (line >> word){
            auto &lines = wm[word];            //必须用引用
            if (!lines)
                lines.reset(new set); //
            lines->insert(n);
        }
    }
}
QueryResult TextQuery::query(const std::string&sought) const{
    static shared_ptr> nodata(new set);
    auto loc = wm.find(sought);
    if (loc == wm.end())
        return QueryResult(sought, nodata, file);
    else
        return QueryResult(sought, loc->second, file);
}
ostream &print(ostream& os, const QueryResult &qr){
    os << qr.sought << " occurs " << qr.lines->size() << " "
       << (qr.lines->size()>1?" times": " time") << endl;
    for (auto num:*qr.lines)
        os << "t(line " << num+1 << ") "
           << *(qr.file->begin() + num) << endl;
    return os;
}

void runQueries(ifstream& infile){
    TextQuery tq(infile);
    while(true){
        cout << "enter word to look for, or q to quit: ";
        string s;
        if (!(cin>>s) || s == "q") break;
        print(cout, tq.query(s)) << endl;
    }
}
int main(){
    ifstream infile("./text.txt");
    runQueries(infile);
return 0;}

总结

  C++中,动态内存通过new表达式分配,通过delete表达式释放。标准库还定义了allocator类来分配动态内存赶块。
尽量不要用手动的方式管理动态,用智能指针
堆:自由空间的同义词
堆:是一棵完全二叉树······

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/846794.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号