• 【c++ primer读书笔记】【第12章】动态内存


    1、在c++中,动态内存管理通过一对运算符完成:new,在动态内存中为对象分配空间并返回一个指向该对象的指针。delete,接受一个动态对象的指针,销毁该对象,并释放与之相关的内存。

    2c++11新标准库提供了两种智能指针类型管理动态对象,智能指针的行为类似常规指针,区别是它自动释放所指向的内存。shared_ptr允许多个指针指向同一个对象,unique_ptr独占所指向的对象,伴随类weak_ptr指向share_ptr所管理的对象。

    3、最安全的使用动态内存的方法是使用一个make_shared的函数。此函数在动态内存中分配一个对象并初始化,返回指向此对象的shared_ptr

    shared_ptr<int> p1 = make_shared<int>(42); //p1是指向一个值为41的int的shared_ptr
    shared_ptr<string> p2 = make_shared<string>(5, '9');//p2指向一个值为“99999”的string
    shared_ptr<int> p3 = make_shared<int>(); //p3指向一个值初始化的int
    

    4、shared_ptr的拷贝和赋值

    shared_ptr<int> p = make_shared<int>(42); //p是指向一个值为42的int的shared_ptr
    auto q(p);//p和q指向相同对象,此对象有两个引用者
    auto r = make_shared<int>(42);
    r = q; //给r赋值,令它指向另一个地址,递增q指向的对象的引用计数,递减r原来指向的对象的引用计数
           //r原来指向的对象已没有引用者,会自动释放
    
    我们可以认为每个shared_ptr都有一个关联的计数器,通常称其为引用计数,无论我们拷贝一个share_ptr,计数器都会递增。当我们给一个shared_ptr赋值或者shared被销毁,计数器就会递减。

    #include<iostream>
    #include<memory>
    using namespace std;
    
    int main(){
    	shared_ptr<int> p = make_shared<int>(42); //p是指向一个值为41的int的shared_ptr
    	cout << "p use_count:" << p.use_count() << endl; //返回与p共享对象的智能指针数量
    	cout << "p unique:" << p.unique() << endl; //若p.use_count()为1,返回true,否则返回false
    
    	auto q(p);//p和q指向相同对象,此对象有两个引用者
    	cout << "q use_count:" << q.use_count() << endl;//返回与q共享对象的智能指针数量
    	cout << "q unique:" << q.unique() << endl;//若q.use_count()为1,返回true,否则返回false
    
    	auto r = make_shared<int>(42);
    	cout << "r use_count:" << r.use_count() << endl;
    	r = q; //给r赋值,令它指向另一个地址,递增q指向的对象的引用计数,递减r原来指向的对象的引用计数
    	       //r原来指向的对象已没有引用者,会自动释放
    	cout << "r use_count:" << r.use_count() << endl; //与r共享对象的智能指针数量为3,分别是p,q,r
    	cout << "q use_count:" << q.use_count() << endl;//与q共享对象的智能指针数量为3,分别是p,q,r
    	 system("pause");
    	 return 0;
    }
    

    5、当指向对象的最后一个shared_ptr被销毁时,shared_ptr 类会自动销毁此对象。它通过析构函数完成销毁工作。shared_ptr 的析构函数会递减它所指向的对象的引用计数,如果引用计数变为0,shared_ptr的函数就会销毁对象,并释放它占用的资源。

    6、一个unique_ptr 拥有它所指向的对象,和shared_ptr不同,某个时刻只能有一个unique_ptr 指向一个给定对象,当unique_ptr 被销毁时,对象也被销毁。

    #include<iostream>
    #include<memory>
    #include<string>
    using namespace std;
    
    int main(){
    	unique_ptr<string> p1(new string("aaa"));
    	//unique_ptr<string> p2(p1);  //错误,unique_ptr不支持拷贝
    	unique_ptr<string> p3;
    	//p3 = p1;     //错误,unique_ptr不支持赋值
    
    	unique_ptr<string> p4(p1.release()); //p1.release()将p1置为空,将所有权从p1转移给p4
    	
    	unique_ptr<string> p5(new string("bbb"));
    	p5.reset(p4.release()); //reset释放了p5原来指向的内存,p5指向了p4原来指向的内存
    
    	cout << *p5 << endl;
    
    	 system("pause");
    	 return 0;
    }
    

    7类似shared_ptr,unique_ptr默认情况下用delete释放它指向的对象。和shared_ptr一样,我们可以重载一个unique_ptr中默认的删除器类型。重载一个unique_ptr中的删除器会影响到unique_ptr类型及如何构造该类型的对象。

    格式:

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

    8、weak_ptr 是一种不控制对象生存期的智能指针,它指向由一个shared_ptr 管理的对象。将weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数,当最后一个指向对象的shared_ptr被销毁,即使有weak_ptr指向该对象,对象还是会被释放。

    9、allocator类

    allocator类将内存分配和对象构造分离开来,它分配的内存是原始的,未构造的。

    #include<iostream>
    #include<memory>
    #include<string>
    using namespace std;
    
    int main(){
    	allocator<string> alloc; //定义了一个名为alloc的allocator对象,它为类型为string的对象分配内存
    	auto const p=alloc.allocate(10); //分配内存保存10个类型为string的对象
    	auto q = p; //q指向最后构造的元素之后的位置
    	alloc.construct(q++, 5, 'c');//*q为ccccc,q指向的内存中构造一个对象
    	alloc.construct(q++);  //*q为空字符串
    	alloc.construct(q++, "hi"); //*q为hi
    
    	cout << *p << endl; //正确,输出ccccc
    	//cout << *q << endl; //错误,q指向未构造的内存
    
    	while (q != p)
    		alloc.destroy(--q); //销毁真正构造的string
    	alloc.deallocate(p, 10);//释放p中地址的内存,这快内存保存了n个string对象,p必须是allocte返回的指针,n必须是allocate(n)的n  
                                //在调用deallocate时必须先毁坏这块内存中创建的对象  
    
    	 system("pause");
    	 return 0;
    }
    

    10、allocator类的拷贝和填充未初始化内存的算法

    #define _SCL_SECURE_NO_WARNINGS  //为了防止VS2013报错
    #include<iostream>
    #include<memory>
    #include<vector>
    using namespace std;
    
    int main(){
    	vector<int> vec{ 1,2,3 };
    	vector<int> vec2{ 5,6,7,8};
    	allocator<int> alloc; 
    
    	auto p1=alloc.allocate(vec.size()*4); 
    
    	auto p2 = uninitialized_copy(vec.begin(), vec.end(), p1); //uninitialized_copy(b,e,b2); 
    	//从迭代器b和e指定的输出范围中拷贝元素到迭代器b2指定的未构造的原始内存中,b2指向内存必须足够大
    	auto p3=uninitialized_copy_n(vec2.begin(), vec2.size(), p2);
    	//uninitialized_copy_n(b, n, b2) 从迭代器b指向的元素开始拷贝n个元素到到以b2开始的内存中
    	auto p4 = uninitialized_fill_n(p3, vec.size(), 10);
    	//uninitialized_fill_n(b, n, t) 从b指向的内存地址创建n个对象,t是填充的元素
    	uninitialized_fill(p4, p4 + 2, 9);
    	//uninitialized_fill(b, e, t)      在b和e指定的原始内存范围中创建对象,对象的值均为t的拷贝
    		
    
    
    	for (size_t i = 0; i < vec.size() * 4; ++i)
    		cout << *p1++ << " ";
    	cout << endl;
    
    	 system("pause");
    	 return 0;
    }

  • 相关阅读:
    统计学习方法学习笔记(一)--极大似然估计与贝叶斯估计原理及区别
    数据过拟合解决方法
    LSTM基础
    异方差产生与解决
    人工免疫相关算法
    Svm相关
    sscanf,sscanf_s及其相关用法
    C语言数组初始化
    生产者和消费者
    Linux线程-创建
  • 原文地址:https://www.cnblogs.com/ruan875417/p/4495570.html
Copyright © 2020-2023  润新知