C++智能指针

智能指针对象是一个对象,可以充当函数参数也可以充当函数返回值。

智能体现在内存管理(内存的申请和释放)(在对象的析构函数中做了资源的释放)

用智能指针管理new的对象将不在需要手动delete

智能指针本质上是一个模板类,一般使用的是这个类的对象,而不是指针(弱化指针)

shared_ptr

  • get() 函数: 返回数据的指针的引用

  • use_count(): 返回的是管理对象的智能指针对象数

  • swap():交换管理对象

  • reset():重置管理对象

    #include<iostream>
    #include<memory>
    #include<string>
    #include<cstdio>
    using namespace std;
    
    class MM {
    public:
    	void print() {
    		cout << "打印函数" << endl;
    	}
    	~MM() {
    		cout << "析构函数被调用" << endl;
    	}
    
    protected:
    
    };
    void printData(shared_ptr<MM>& pMM) {//这里用引用就不会产生拷贝本,下面的use_count数就不会变
    	pMM->print();
    	cout << "管理该对象的指针个数:" << pMM.use_count() << endl;
    }
    
    void freeFile(FILE* file) {
    	free(file);
    	cout << "文件释放成功" << endl;
    }
    
    int main() {
    	//1.基本用法
    	//1.1初始化
    	shared_ptr<int> pInt(new int(1111));
    	//不能采用赋值的方式初始化
    	//shared_ptr<int> pNum = new int(111);	错误
    	//1.2使用(访问数据)
    	cout << *pInt << endl;
    	//2.自定义类型测试自动delete
    	{	//测试对象死亡的时候是否调用了析构函数,用一个作用域分辨符来括起来
    		shared_ptr<MM> pMM(new MM);
    
    		//MM* p = new MM;	这个就需要手动释放内存
    		pMM->print();
    		MM* pM = pMM.get();	//返回new MM的地址
    	}
    	//3.常用成员函数
    	//get()函数
    	//这里管理int型的数据,get函数就可以返回int*类型的指针
    	int* pData = pInt.get();
    
    	//use_count()函数
    	shared_ptr<int> a(new int(10));
    	cout << "正常访问:" << *a << endl;
    	cout << "get读取数据:" << *a.get() << endl;
    	cout << "管理该对象的指针个数:" << a.use_count() << endl;
    	//通过上面的指针做拷贝构造
    	shared_ptr<int> b(a);//意味着b这个指针也是管理10这个整数的
    	cout << "管理该对象的指针个数:" << a.use_count() << endl;
    	cout << "管理该对象的指针个数:" << b.use_count() << endl;
    
    	shared_ptr<int> c;
    	if (!c) {
    		cout << "空智能指针对象" << endl;
    	}
    	c = b;
    	cout << "管理该对象的指针个数:" << c.use_count() << endl;
    
    	shared_ptr<int> aa(new int(12));
    	shared_ptr<int> bb(new int(21));
    	cout << "aa" << *aa << "\tbb" << *bb << endl;
    	aa.swap(bb);
    	cout << "aa" << *aa << "\tbb" << *bb << endl;
    	bb.reset(new int(123));
    	cout << "new bb\t" << *bb << endl;
    	{
    		cout << "当作函数参数。。。。" << endl;
    		shared_ptr<MM> pMM(new MM);
    		cout << "管理该对象的指针个数:" << pMM.use_count() << endl;
    		printData(pMM);
    	}
    
    
    	//4.带删除器的写法
    	//删除器:理解为自己写释放内存的过程
    	//有些内存不是正常new出来的内存,但也算是动态内存,这时候就要手动写释放过程,如用智能指针管理对象数组,包括c语言里面的文件操作
    	{
    
    		cout << "删除器写法————" << endl;
    		//shared_ptr<MM> p(new MM[4]);	报错,正常的不能释放一段内存
    		shared_ptr<MM> p(new MM[4], [](MM* array) {delete[] array; });
    	}
    
    	//c语言文件操作
    	shared_ptr<FILE> pf(fopen("1.txt", "w"),freeFile);
    
    	//通过make_shared<int>()
    	shared_ptr<int> pMake = make_shared<int>(12);
    	cout << *pMake << endl;
    	//make_shared如果用来管理自定义类型,那么参数个数由构造函数决定
    	
    	//当我们用get方法去获取指针的时候,一定不能手动释放,否则会重复析构
    	int* ppp = pMake.get();
    	//delete[] ppp;
    
    
    	return 0;
    }

weak_ptr

  • 弱引用指针,不会累计计数

  • weak_ptr只能通过原有的shared_ptr或者weak_ptr来构造,不能直接管理数据

  • 应用场景较为单一

  • 主要应用场景: 为了解决shared_ptr 循环引用内存导致无法释放问题

  • 不可使用* 取值,只能使用->取值

  • 通过成员函数lock获取shared_ptr对象 然后再访问数据

    #include<iostream>
    #include<memory> 
    using namespace std;
    
    class MM {
    public:
    	MM(){}
    	void print() {
    		cout << "打印函数" << endl;
    	}
    	~MM() { cout << "析构函数" << endl; }
    protected:
    
    
    };
    class B;
    class A {
    public:
    	A() { cout << "A"; }
    	~A(){ cout << "~A"; }
    	weak_ptr<B> b;
    protected:
    };
    class B {
    public:
    	B() { cout << "B"; }
    	~B() { cout << "~B"; }
    	weak_ptr<A> a;
    protected:
    };
    void testLoopUse() {
    	cout << "循环引用————" << endl;
    	shared_ptr<A> aObject(new A);
    	shared_ptr<B> bObject(new B);
    	aObject->b = bObject;
    	bObject->a = aObject;
    	cout << endl <<"计数:" << bObject.use_count() << endl;
    	cout << endl;
    }
    
    int main() {
    	shared_ptr<MM> pMM(new MM);
    	shared_ptr <MM> pMM2 = pMM;
    	cout << "use_count:" << "\t" << pMM.use_count() << endl;
    	weak_ptr<MM> pwMM(pMM);
    	cout << "use_count:" << "\t" << pMM.use_count() << endl;
    	//(*pwMM).print();	报错
    	pwMM.lock().get()->print();
    	//pwMM.lock()获取的是share_ptr对象
    	pwMM.lock()->print();
    	{
    		cout << "循环引用————" << endl;
    
    	}
    	testLoopUse();
    	return 0;
    }

unique_ptr

  • 禁止拷贝和赋值,独占型

  • 任何时候unqiue_ptr操作管理对象,永远都只有一个有效

    • 可以通过move函数转交所有权

    • reset函数结合release函数移交所有权

      #include<iostream>
      #include<string>
      #include<memory>
      using namespace std;
      class MM {
      public:
      	MM(){}
      	MM(int num):num(num){}
      	int getNum() {
      		return num;
      	}
      	~MM() {
      		cout << "MM:" << num << endl;
      	}
      protected:
      	int num=0;
      };
      struct deleteMM
      {
      	void operator()(MM* mm) {
      		delete[] mm;
      		cout << "释放成功————" << endl;
      	}
      };
      
      int main() {
      	unique_ptr<MM> pMM(new MM(1111));
      	//错误操作
      	//unique_ptr<MM> pMM2 = pMM;
      	unique_ptr<MM> pMM3;
      	//pMM3 = pMM;
      
      	//访问数据
      	cout << "MM num:" << pMM.get()->getNum() << endl;
      	cout << "MM num:" << pMM->getNum() << endl;
      	//如果真的需要拷贝赋值,智能通过移动拷贝或者move转交所有权
      	//移动拷贝
      
      	//move转交所有权
      	pMM3 = move(pMM);	//pMM不在存在
      	cout << "MM num:" << pMM3->getNum() << endl;
      	unique_ptr<MM> pMM4(move(pMM3));	//MM&&移动构造
      
      	//移动后可以重新构造
      	pMM.reset(new MM(123));
      	cout << "MM num" << pMM->getNum() << endl;
      	unique_ptr<MM> pMM5;
      	pMM5.reset(pMM4.release());
      	cout << pMM5->getNum() << endl;
      	//删除器
      	{
      		unique_ptr<MM, deleteMM> mmArray(new MM[4]);
      		unique_ptr<MM, void(*)(MM*)> pp(new MM[3], [](MM* mm) {delete[] mm; });
      	}
      
      	return 0;
      }

      C++正则表达式

      正则是一种规则,它用来匹配(进而捕获、替换)字符串。这种规则需要“模式”、“字符串”这两样东西,“模式”根据正则规则,来处理“字符串”。这种规则被许多语言支持,C++11以后才支持正则。

      具有特殊意义的元字符

      \:\字符能够改变字符原本的含义

      ^:^字符指示字符串的头,且要求字符串以字符开头,不占位。\^表示一个真正的^符号。

      $:$字符指示字符串的尾,且要求字符串以字符结尾,不占位。\$表示一个真正的$符号。

      ():分组,大正则中包含小正则。可以改变默认的优先级。在模式中可以使用\1来表示第一组已然捕获到的东西。

      \b:指示字符串的边界(头/尾/空格左/空格右),字符\b要求边界的左边是字符,\b字符要求边界的右边是字符。

      .:表示一个除了\n以外的任意一个字符。\.表示一个真正的.符号。

      |:a|b a或b之一

      [abc]:abc之中的任意一个

      [^abc]: abc之外的

      [a-z]: 任意小写字母

      [^a-z]: 除了小写字母之外的

      \w:任意一个字母数字下划线,等价于[(0-9)(a-z)(A-Z)(_)]

      \W:字母数字下划线之外的,等价于[]

      \d: 任意一个数子

      \D: 除了数字之外的

      \s: 空白符(空格、制表符、换页符)

      量词元字符

      *:字符*要求字符出现0到多次 {0,}

      +:字符+要求字符出现1到多次 (\w) {1,}

      ?:字符?要求字符出现0次或1次 {0,1}

      {n}:字符{n}要求字符出现n次

      {n,}:字符{n,}要求字符出现n到多次 {0,}

      {n,m}:字符{n,m}要求字符出现n到m次、

      所以含有\的元字符,在C++定义时,都要写成\\

      校验数字的表达式(注意使用时去掉空格,单斜杠变双斜杠)

      数字:^ [0 - 9] * $ n位的数字:^ \d{ n }$ 至少n位的数字:^ \d{ n, }$ m - n位的数字: ^ \d{ m,n }$ 零和非零开头的数字: ^ (0 | [1 - 9][0 - 9] *)$ 非零开头的最多带两位小数的数字: ^ (\[1 - 9][0 - 9] *) + (.[0 - 9]{ 1,2 }) ? $ 带1 - 2位小数的正数或负数: ^ (\ - ) ? \d + (\.\d{ 1,2 }) ? $ 正数、负数、和小数: ^ (\ - | \ + ) ? \d + (\.\d + ) ? $ 有两位小数的正实数: ^ [0 - 9] + (.[0 - 9]{ 2 }) ? $ 有1~3位小数的正实数: ^ [0 - 9] + (.[0 - 9]{ 1,3 }) ? $ 非零的正整数: ^ [1 - 9]\d * $ 或 ^ ([1 - 9][0 - 9] *) { 1, 3 }$ 或^ \ + ? \[1 - 9][0 - 9] * $ 非零的负整数: ^ \ - [1 - 9][]0 - 9"$ 或 ^-[1-9]\d$ 非负整数: ^ \d + $ 或 ^ [1 - 9]\d * | 0$ 非正整数: ^ -[1 - 9]\d * | 0$ 或 ^ ((-\d + ) | (0 + ))$ 非负浮点数: ^ \d + (.\d + ) ? $ 或 ^ [1 - 9]\d * .\d * | 0.\d * [1 - 9]\d * | 0 ? .0 + | 0$ 非正浮点数: ^ ((-\d + (.\d + ) ? ) | (0 + (.0 + ) ? ))$ 或 ^ (-([1 - 9]\d * .\d * | 0.\d * [1 - 9]\d*)) | 0 ? \.0 + | 0$ 正浮点数: ^ [1 - 9]\d * .\d * | 0.\d * [1 - 9]\d * $ 或 ^ (([0 - 9] + .[0 - 9] * [1 - 9][0 - 9] *) | ([0 - 9] * [1 - 9][0 - 9] * .[0 - 9] + ) | ([0 - 9] * [1 - 9][0 - 9] *))$ 负浮点数: ^ -([1 - 9]\d * .\d * | 0.\d * [1 - 9]\d*)$ 或 ^ (-(([0 - 9] + .[0 - 9] * [1 - 9][0 - 9] *) | ([0 - 9] * [1 - 9][0 - 9] * .[0 - 9]) | ([0 - 9] * [1 - 9][0 - 9] *)))$ 浮点数: ^ (-? \d + )(.\d + ) ? $ 或 ^ -? ([1 - 9]\d * .\d * | 0.\d * [1 - 9]\d * | 0 ? .0 + | 0)$

      校验字符的表达式

      汉字: ^ [\u4e00 - \u9fa5]{ 0, }$ 英文和数字: ^ [A - Za - z0 - 9] + $ 或 ^ [A - Za - z0 - 9]{ 4,40 }$ 长度为3 - 20的所有字符: ^ .{3, 20}$ 由26个英文字母组成的字符串: ^ [A - Za - z] + $ 由26个大写英文字母组成的字符串: ^ [A - Z] + $ 由26个小写英文字母组成的字符串: ^ [a - z] + $ 由数字和26个英文字母组成的字符串: ^ [A - Za - z0 - 9] + $ 由数字、26个英文字母或者下划线组成的字符串: ^ \w + $ 或 ^ \w{ 3,20 }$ 中文、英文、数字包括下划线: ^ [\u4E00 - \u9FA5A - Za - z0 - 9_] + $ 中文、英文、数字但不包括下划线等符号: ^ [\u4E00 - \u9FA5A - Za - z0 - 9] + $ 或 ^ [\u4E00 - \u9FA5A - Za - z0 - 9]{ 2,20 }$ 可以输入含有 ^ %&',;=?$"等字符:[^%&', ; = ? $\x22] + 12 禁止输入含有~的字符:[^ ~\x22] +

      特殊需求表达式

      Email地址: ^ \w + ([-+.]\w + ) * @\w + ([-.]\w + ) * .\w + ([-.]\w + ) * $ 域名:[a - zA - Z0 - 9][-a - zA - Z0 - 9]{ 0,62 }(/ .[a - zA - Z0 - 9][-a - zA - Z0 - 9]{ 0,62 }) + / . ? InternetURL:[a - zA - z] + ://\s* 或 ^http://([\w-]+.)+[\w-]+(/[\w-./?%&=])?$ 手机号码: ^ (13[0 - 9] | 14[5 | 7] | 15[0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9] | 18[0 | 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9])\d{ 8 }$ 电话号码(0511 - 4405222、021 - 87888822):\d{ 3 } - \d{ 8 } | \d{ 4 } - \d{ 7 } 身份证号(15位、18位数字): ^ \d{ 15 } | \d{ 18 }$ 短身份证号码(数字、字母x结尾): ^ ([0 - 9]) { 7, 18 }(x | X) ? $ 或 ^ \d{ 8,18 } | [0 - 9x]{ 8,18 } | [0 - 9X]{ 8,18 } ? $ 帐号:(字母开头,允许5 - 16字节,允许字母数字下划线): ^ [a - zA - Z][a - zA - Z0 - 9_]{ 4,15 }$ 密码:(以字母开头,长度在6~18之间,只能包含字母、数字和下划线): ^ [a - zA - Z]\w{ 5,17 }$ 强密码(必须包含大小写字母和数字的组合,不能使用特殊字符,长度在8 - 10之间): ^ (? = .\d)(? = .[a - z])(? = .*[A - Z]).{8, 10}$ 日期格式: ^ \d{ 4 } - \d{ 1,2 } - \d{ 1,2 } 一年的12个月(01~09和1~12): ^ (0 ? [1 - 9] | 1[0 - 2])$ 一个月的31天(01~09和1~31): ^ ((0 ? [1 - 9]) | ((1 | 2)[0 - 9]) | 30 | 31)$ xml文件: ^ ([a - zA - Z] + -? ) + [a - zA - Z0 - 9] + \\.\[x | X]\[m | M][l | L]$ 中文字符的正则表达式:[\u4e00 - \u9fa5] 双字节字符:\[^ \x00 - \xff](包括汉字在内,可以用来计算字符串的长度(一个双字节字符长度计2,ASCII字符计1)) 空白行的正则表达式:\n\s * \r(可以用来删除空白行) HTML标记的正则表达式:<(\S* ? ) > >. ? < / \1> | <.* ? / > (复杂的嵌套标记依旧无能为力) 首尾空白字符的正则表达式: ^ \s * | \s * $或(^ \s*) | (\s * $) (可以用来删除行首行尾的空白字符(包括空格、制表符、换页符等等)) 腾讯QQ号:[1 - 9][0 - 9]{ 4, } (腾讯QQ号从10000开始) 中国邮政编码:[1 - 9]\d{ 5 }(? !\d) (中国邮政编码为6位数字) IP地址:\d + .\d + .\d + .\d + (提取IP地址时有用) IP地址:((? : (? : 25[0 - 5] | 2[0 - 4]\d | [01] ? \d ? \d)\.) { 3 }(? : 25[0 - 5] | 2[0 - 4]\d | [01] ? \d ? \d))

      #include<regex>
      #include<iostream>
      #include<cstdbool>
      #include<string>
      using namespace std;
      void test_regex_match() {
      #if 0
      	//1.基本单元的组成
      	//2.复制过程中\改成
      	//匹配  返回值布尔类型,返回两个参数,一个是要匹配的字符串,一个是正则规则
      	//regex_match(string str, regex reg)
      	//匹配返回:true false
      	regex reg("^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$");
      	string userName;
      	while (1) {
      		cout << "请输入一个邮箱:";
      		cin >> userName;
      		bool result = regex_match(userName, reg);
      		if (result == true) {
      			cout << "正确邮箱" << endl;
      			break;
      		}
      	}
      #endif
      
      	//正则匹配一定是完全匹配,一点不一样的都不行
      	string str = "Iloveyou1314";
      	regex reg("[a-z0-9]+");
      	if (regex_match(str, reg)) {
      		cout << "匹配" << endl;
      	}
      	else {
      		cout << "不匹配" << endl;	//I不满足正则规则是大写的
      	}
      
      
      	//大小写问题,c++提供带参构造(构造的时候可以忽略大小写)
      	regex reg2("[a-z0-9]+", regex_constants::icase);
      	if (regex_match(str, reg2)) {
      		cout << "匹配" << endl;
      	}
      	else {
      		cout << "不匹配" << endl;	
      	}
      	//字符串也可以直接充当正则规则
      	regex reg3("Iloveyou");
      	//必须要一模一样才匹配
      	if (regex_match(str, reg3)) {
      		cout << "匹配" << endl;
      	}
      	else {
      		cout << "不匹配" << endl;	//1314不满足
      	}
      }
      //正则替换 regex_replace(string str,regex reg,string newstr);
      void test_regex_replace() {
      	string str = "ILoveyou1314IMissyou520me";
      	regex reg("\\d+");	//多个数字
      	cout << "str;" << regex_replace(str, reg, "我爱你") << endl;	//不改变原字符串
      	//如何控制替换
      	//只替换第一次出现的
      	cout << "only first:" << regex_replace(str, reg, "我爱你", regex_constants::format_first_only) << endl;
      	//不拷贝
      	cout << "no copy:" << regex_replace(str, reg, "我爱你", regex_constants::format_no_copy) << endl;
      	//默认方式
      	cout << "default:" << regex_replace(str, reg, "我爱你", regex_constants::format_default) << endl;	//(不传参)
      
      }
      
      //截取(捕获)处理字符串
      //bool regex_search(string str,smatch result,regex reg)
      void test_regex_search() {
      	//正常截取处理 匹配项
      	string str = "ILoveyou1314IMissyou520me";
      	smatch result;
      	bool flag = regex_search(str, result, regex("\\d+"));
      	if (flag) {
      		cout << "size:" << result.size() << endl;
      		for (auto v : result) {
      			cout << v.str() << endl;
      		}
      		cout << "pre:" << result.prefix() << endl;
      		cout << "suf:" << result.suffix() << endl;
      
      	}
      	cout << "原来的str:" << str << endl;
      	//一直匹配完,可以用result.suffix()作为循环条件,没有返回空字符串
      	//srgex_iterator
      	regex rule("\\d+");
      	sregex_iterator pos(str.begin(), str.end(), rule);
      	//对于这个函数的无参构造,就是一个结束条件
      	sregex_iterator end;	//end_of_iterator
      	while (pos != end) {
      		cout << pos->str() << endl;
      		pos++;
      	}
      	//按照正则规则拆解每一部分字符串
      	//sregex_token_iterator(iterator.begin,iterator.end,regex regex,int flag)
      	//flag 0:所有匹配到的  flag -1:存储所有不匹配的
      	regex regexRule("\\d+");
      	sregex_token_iterator Begin(str.begin(), str.end(), rule, -1);
      	sregex_token_iterator End;
      	while (Begin != End) {
      		cout << Begin->str() << endl;
      		Begin++;
      	}
      
      
      
      }
      
      
      int main() {
      	test_regex_match();
      	test_regex_replace();
      	test_regex_search();
      	return 0;
      
      }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++智能指针:auto_ptr详解 指针,相信⼤家并不陌⽣。⽆论是我们在进⾏查看内存还是在修改字符串,我们都会⽤到指针。 最常见的情况则是我们使⽤malloc或者new申请到了⼀块内存,然后⽤⼀个指针来保存起来。我们都知道有内存的申请那就必须要对它进⾏ 释放的处理,否则会造成最严重的后果——内存泄漏。⼀个或者两个申请的内存我们或许不会忘记去释放,但是如果成千上万⾏代码,你还 能记得住哪个释放了哪个没有释放吗? ⽽智能指针就是为了解决这个问题⽽产⽣的。最开始智能指针是在boost库中的,随着时间发展现在已经成为了C11的特性。(虽然我们本 篇要介绍的最基础的auto_ptr在C++11中已经被unique_ptr替代了。。) 智能指针的基本原理 智能指针其实是⼀个类,可以通过将普通指针作为参数传⼊智能指针的构造函数实现绑定。只不过通过运算符重载让它"假装"是⼀个指 针,也可以进⾏解引⽤等操作。既然智能指针是⼀个类,对象都存在于栈上,那么创建出来的对象在出作⽤域的时候(函数或者程序结束)会 ⾃⼰消亡,所以在这个类中的析构函数中写上delete就可以完成智能的内存回收。 Auto_ptr详解 使⽤时,需要包含头⽂件:memory。 auto_ptr,作为智能指针的始祖,能基本实现我们所期望的功能。⽽且设计简单源码易懂,虽然缺陷众多,但作为了解智能指针的研究对象 还是⼗分合适的。 ⾸先我们先来写⼀个测试类⽤于分析。 #include <iostream> #include <memory> using namespace std; class Test { public: Test(int param = 0) //调⽤时,可以指定是第⼏个对象。默认第0个 { num = param; cout << "Simple:" << num << endl; } ~Test() //析构时会指出第⼏个对象被析构。 { cout << "~Simple:" << num << endl; } void PrintSomething() //输出附加的字符串⽅法。 { cout << "PrintSomething:" << info_extend.c_str() << endl; } int num; //代表这个对象的序号。 string info_extend; //附加的字符串。 }; 接下来,我们通过⼏个测试函数来实验⼀下auto_ptr的基本功能,再来了解⼀下它的弊端。 I.Auto_ptr的基本功能 的基本功能 void TestAutoPtr1() { auto_ptr<Test> my_auto (new Test(1)); //绑定⼀个Test类的新建对象 if(my_auto.get()) //get函数⽤来显式返回它拥有的对象指针,此处判⾮空 { my_auto->PrintSomething(); my_auto.get()->info_extend = "Addition"; //对类内成员进⾏操作 my_auto->PrintSomething(); //看是否成功操作 (*my_auto).info_extend += " other"; //实验解引⽤操作 mt_auto->PrintSomething(); } } 运⾏结果: 我们可以看到在绑定时输出Simple:1,之后也能正常实现Test类中的功能,同时my_auto可以通过get⽅法进⾏裸指针赋值以及使⽤*进⾏ 解引⽤操作,与普通指针⽆异。最后函数结束后,在调⽤析构函数的同时auto_ptr的析构函数也将Test类的对象delete掉,我加⼊的内存泄 漏探测⼯具也显⽰No memory leaks detected(没有检查测到内存泄漏). II.Auto_ptr的弊端 的弊端1 void TestAutoPtr2() { auto_ptr<Test> my_auto1 (new Test(1)); if(my_auto1.get()) { (*my_auto1).info_extend = "Test"; //先赋予⼀个初始值作为区别两个指针的标志 auto_ptr<Test> my_auto2; my_auto2 = my_auto1; my_auto2->PrintSomething(); //打印发现成功转移 my_auto1->PringSomething(); //崩溃 } } 在这个程序的最后⼀步会崩溃,罪魁祸⾸就是my_auto2 = my_auto1语句。 autp_ptr对赋值运算符重载的实现是reset(Myptr.release()),即reset和release函数的组合。release会释放所有权,reset则是⽤于接受
C++智能指针 ⼀ ⼀. 智能指针 智能指针 在 C++ 中,我们申请内存⼀般使⽤ new,释放内存时使⽤ delete,但是有时候我们可能由于疏忽会忘记 delete,或者当程序有多个 出⼝时,也会容易有 new 没有 delete,这样就产⽣了内存泄漏,如果你的程序是⼀个需要长期运⾏的服务器程序,可能运⾏着⼏天突然程 序就崩溃了,原因也不好定位,所以为了⽅便内存管理,C++ 引⼊了智能指针智能指针的优点在于能够帮助程序员⾃动释放我们 new 出 来的堆内存。 C++ 标准库有四种智能指针:auto_ptr,unique_ptr,shared_ptr,weak_ptr(auto_ptr 是 C++98 标准的,其余都是 C++11 标准推出的,auto_ptr 现在已经不再使⽤了),C++11 这三种智能指针都是类模板。 ⼆ ⼆. shared_ptr (⼀)概述 (⼀)概述 shared_ptr 是⼀个共享式指针,所有的 shared_ptr 共享对指向内存的所有权,不是被⼀个 shared_ptr 拥有,⽽是多个 shared_ptr 之间互相协作。 (⼆)⼯作原理 (⼆)⼯作原理 引⽤计数,use_count 为 0 时就释放对象空间。 (三)初始化 (三)初始化 如果是定义了⼀个智能指针却不初始化,shared_ptr<int> p1,代表定义了⼀个指向 int 类型对象的智能指针但是⽬前指向为 empty。推荐使⽤ make_shared 函数来初始化 shared_ptr,它是标准库的函数模板,安全,⾼效地分配和使⽤ shared_ptr。 shared_ptr<int> pint = make_shared<int>(100); shared_ptr<string> pstr = make_shared<string>(5,'a'); 也可以使⽤直接初始化的⽅式 shared_ptr<int> pint(new int(100)) 来创建⼀个 shared_ptr 并初始化,但是由于 shared_ptr 定义 的构造函数是 explicit 的,因此不能使⽤ shared_ptr<int> pint = new int(100) 来创建⼀个 shared_ptr 并初始化,因为这种⽅式隐式要 求将⼀个普通的 int * 转换为 shared_ptr<int>。 (四)常⽤操作 (四)常⽤操作 1. use_count 返回多少个智能指针指向该对象,主要⽤于调试。 2. unique 判断该智能指针是否独占该内存,如果该智能指针不指向任何对象,判断 unique 的时候也是假。 3. reset 不带参数时:放弃指针对对象的掌管权,重置为 nullptr 带参数时:参数⼀般是⼀个 new 的空间,相当于放弃指针对当前对象的掌管权,然后将指针指向 new 出来的空间。 4. * 解引⽤,可以获取指针指向的对象。 5. get 获取指针指针⾥保存的裸指针,⼀般⽤于⼀些接⼝需要使⽤到 C 语⾔指针的情况。 6. swap 交换两个智能指针所指的对象。 7. =nullptr 该智能指针指向 nullptr,代表解除对该对象的掌握权,引⽤计数将会减1,如果此时该内存空间的引⽤计数变为0,会同时释放该内存。 8. 指定删除器以及删除数组问题 指定删除器以及删除数组问题 智能指针能在⼀定时机帮我们删除所指向的对象,使⽤ delete 作为默认的资源析构⽅式,我们也可以指定⾃⼰的删除器取代系统提供的默 认删除器,当智能指针需要删除所指向的对象时,编译器就会调⽤我们提供的删除器。 shared_ptr 指定删除器的⽅法⽐较简单,⼀般只需要在参数中添加具体的删除器函数即可。如果提供了删除器,那么就需要⼿动删除资 源,否则会造成内存泄漏。删除器函数可以是函数,lambda 表达式,重载了 operator() 的类等。 还可以使⽤ default_delete 来做删除器,default_delete 是标准库⾥的⼀个模板类。如:shared_ptr<A> p3(new A[10], default_delete<A[]>()),这样就知道我们使⽤智能指针指向了⼀个对象数组,这样就可以正确释放了。 其实,使⽤ shared_ptr 指向对象数组不需要通过删除器的⽅式,只需要在定义 shared_ptr 时指为数组类型即可,如:shared_ptr<A[]> p4(new A[10])。 额外说明:就算是两个 shared_ptr 指定了不同的删除器,只要他们指向的对象类型相同,那么这两个 shared_ptr 也是同⼀个类型,可以 放到同⼀个容器去,vector<shared_ptr<int>> pvec{p1,p2}。 三 三
C++智能指针 智能指针 智能指针概念 C/C++ 语⾔最为⼈所诟病的特性之⼀就是存在内存泄露问题,因此后来的⼤多数语⾔都提供了内置内存分配与释放功能,有的甚⾄⼲脆对语 ⾔的使⽤者屏蔽了内存指针这⼀概念。这⾥不置贬褒,⼿动分配内存与⼿动释放内存有利也有弊,⾃动分配内存和⾃动释放内存亦如此,这 是两种不同的设计哲学。有⼈认为,内存如此重要的东西怎么能放⼼交给⽤户去管理呢?⽽另外⼀些⼈则认为,内存如此重要的东西怎么能 放⼼交给系统去管理呢?在 C/C++ 语⾔中,内存泄露的问题⼀直困扰着⼴⼤的开发者,因此各类库和⼯具的⼀直在努⼒尝试各种⽅法去检 测和避免内存泄露,如 boost,智能指针技术应运⽽⽣。 智能指针主要⽤于管理在堆上分配的内存,它将普通的指针封装为⼀个栈对象。当栈对象的⽣存周期结束后,会在析构函数中释放掉申请的 内存,从⽽防⽌内存泄漏。简要的说,智能指针利⽤了 C++ 的 RAII 机制,在智能指针对象作⽤域结束后,会⾃动做内存释放的相关操作, 不需要我们再⼿动去操作内存。 RAII是C++的发明者Bjarne Stroustrup提出的概念,RAII全称是"Resource Acquisition is Initialization",直译过来是"资源获取即初始化",也 就是说在构造函数中申请分配资源,在析构函数中释放资源。因为C++的语⾔机制保证了,当⼀个对象创建的时候,⾃动调⽤构造函数,当 对象超出作⽤域的时候会⾃动调⽤析构函数。所以,在RAII的指导下,我们应该使⽤类来管理资源,将资源和对象的⽣命周期绑定。 C++ 中有四种智能指针:auto_pt、unique_ptr、shared_ptr、weak_ptr 其中后三个是 C++11 ⽀持,第⼀个已经被 C++11 弃⽤且被 unique_prt 代替,不推荐使⽤。下⽂将对其逐个说明。 std::auto_ptr 在这个年代讨论 std::auto_ptr 不免有点让⼈怀疑是不是有点过时了,确实如此,随着 C++11 标准的出现(最新标准是 C++20),std::auto_ptr 已经被彻底放弃,取⽽代之是 std::unique_ptr。然⽽,之所以还向介绍⼀下 std::auto_ptr 的⽤法以及它的设计不⾜ 之处是想更多了解 C++ 语⾔中智能指针的发展过程,⼀项技术如果我们了解它过去的样⼦和发展的轨迹,我们就能更好地掌握它。 std::auto_ptr 的基本⽤法如下代码所⽰: #include <iostream> #include <memory> using namespace std; int main() { //初始化⽅式1 std::auto_ptr<int> ap1(new int(8)); //初始化⽅式2 std::auto_ptr<int> ap2; ap2.reset(new int(8)); cout << *ap1 << ", " << *ap2 << endl; return 0; } 输出: 8, 8 智能指针对象 ap1 和 ap2 均持有⼀个在堆上分配 int 对象,其值均是 8,这两块堆内存均可以在 ap1 和 ap2 释放时得到释放。这是 std::auto_ptr 的基本⽤法。 std::auto_ptr 真正让⼈容易误⽤的地⽅是其不常⽤的复制语义,即当复制⼀个 std::auto_ptr 对象时(拷贝复制或 operator= 复制),原对象 所持有的堆内存对象也会转移给复制出来的对象。⽰例代码如下: #include <memory> int main() { //测试拷贝构造 std::auto_ptr<int> ap1(new int(8)); std::auto_ptr<int> ap2(ap1); if (ap1.get() != NULL) { std::cout << "ap1 is not empty." << std::endl; } else { std::cout << "ap1 is empty." << std::endl; } if (ap2.get() != NULL) { std::cout << "ap2 is not empty." << std::endl; } else { std::cout << "ap2 is empty." << std::endl; } //测试赋值构造 std::auto_ptr<int> ap3(new int(8)); std::auto_ptr<int> ap4; ap4 = ap3; if (ap3.get() != NULL) { std::cout << "ap3 is not empty." << std::endl; }
C++智能指针:shared_ptr⽤法详解 C++智能指针:shared_ptr⽤法详解 shared_ptr是C++11⾥的新特性,其包装了new操作符在堆上分配的动态对象。如: shared_ptr<int> sp1(new int(100)); //相当于 //int *sp1=new int(100); //auto sp1=make_shared<int>(100); 它与普通指针相⽐,最⼤的不同点就是shared_ptr是⼀个类,当对象⽣命周期结束时,会⾃动调⽤其析构函数,释放内存。⽽不再需要程 序员显⽰地调⽤delete关键字。 同时,shared_ptr重载了"*"和"->"操作符以模仿原始指针的⾏为,并且提供了显⽰bool类型转换以判断指针的有效性。 shared_ptr<int> sp1(new int(100)); assert(sp1); *sp1=200; shared_ptr<string> sp2(new string("Hello")); assert(sp2->size()==5); 我们还可以使⽤shared_ptr的成员函数get()获取原始指针 shared_ptr<int> sp1(new int(100)); int *Int_ptr=sp1.get(); shared_ptr⾥的reset()函数 shared_ptr⾥有个成员函数use_count(),⽤于返回该对象的引⽤计数。 shared_ptr<int> sp1(new int(100)); cout<<"当前计数: "<<sp1.use_count()<<endl; auto sp2=sp1; cout<<"当前计数: "<<sp1.use_count()<<endl; { auto sp3=sp2; cout<<"当前计数: "<<sp1.use_count()<<endl; } cout<<"当前计数: "<<sp1.use_count()<<endl; 当⼀个shared_ptr对象调⽤reset()函数时,它的作⽤时将引⽤计数减⼀,调⽤本⾝的对象的引⽤计数变为0. shared_ptr<int> sp1(new int(100)); cout<<"当前计数: "<<sp1.use_count()<<endl; auto sp2=sp1; cout<<"当前计数: "<<sp1.use_count()<<endl; { auto sp3=sp2; cout<<"当前计数: "<<sp1.use_count()<<endl; } cout<<"当前计数: "<<sp1.use_count()<<endl; sp2.reset();//这⾥换成sp1.reset(),可以发现sp2的计数为1,sp1的计数为0. cout << "sp2当前计数: " << sp2.use_count() << endl; cout << "sp1当前计数: " << sp2.use_count() << endl; 上⾯代码运⾏后,sp2的计数为0,sp1的计数为1。若将sp2.reset()换位sp1.reset(),则sp2的计数为1,sp1的计数为0。 在每次对shared_ptr进⾏拷贝或者赋值的时候,都会使计数加1。 ⼯⼚函数 每次使⽤shared_ptr都需要显⽰的使⽤new关键字创建⼀个对象。固std库提供了⼀个⼯⼚函数make_shared()。 ⽤法: auto sp1=make_shared<int>(100); //相当于 shared_ptr<int> sp1(new int(100)); make_shared是⼀个泛型,<>⾥⾯为数据类型,()对应着new()⾥的东西,其返回值是⼀个shared_ptr类型的变量。 定制删除器 shared_ptr的构造函数可有多个参数,其中有⼀个是shared_ptr(Y *p,D d),第⼀个参数是要被管理的指针,它的含义与其构造函数的参 数相同。⽽第⼆个参数则告诉shared_ptr在析构时不要使⽤delete来操作指针p,⽽要⽤d来操作,即把delete p 换成d(p)。因此,我们 就可以⾃⼰制作⼀个删除器 如:对于传统的struct FILE的C⽂件操作,需要 FILE *fp=fopen("./1.txt","r"); //... //... fclose(fp); 若⽤shared_ptr,则可以这样做: shared_ptr<FILE> fp(fopen("./1.txt","r"), fclose); 离开作⽤域时,shared_ptr会⾃动调⽤fclose()函数关闭⽂件。
C++智能指针详解 智能指针详解 智能指针内容很多,重点是基本⽤法。 #include <boost/shared_ptr.hpp> class CBase: public boost::enable_shared_from_this<CBase> { public: virtual void f(){}//必须有个虚函数才能向上向下转换。 } typedef boost::shared_ptr<CBase> CBasePtr; class CChild: public CBase {} typedef boost::shared_ptr<CChild> CChildPtr; void main() { CBasePtr ptrBase = boost::make_shared<CBase>(); //CBasePtr ptrBase = CBasePtr(new CBase()); // 向下转换 CChildPtr ptrChild = boost::dynamic_pointer_cast<CChild>(ptrBase); // 向上转换 CBasePtr ptrXXX = ptrChild; // 普通转换 CChildPtr ptrXX = CChildPtr(dynamic_cast<CChild*>(ptrXXX.get())); } 暂时学会这些⽤法即可。 url: C++ 智能指针详解 ⼀、简介 由于 C++ 语⾔没有⾃动内存回收机制,程序员每次 new 出来的内存都要⼿动 delete。程序员忘记 delete,流程太复杂,最终导致没 有 delete,异常导致程序过早退出,没有执⾏ delete 的情况并不罕见。 ⽤智能指针便可以有效缓解这类问题,本⽂主要讲解参见的智能指针的⽤法。包 括:std::auto_ptrboost::scoped_ptr、boost::shared_ptr、boost::scoped_array、、boost::weak_ptr、boost::intrusive_ptr。你可能会想,如 此多的智能指针就为了解决new、delete匹配问题,真的有必要吗?看完这篇⽂章后,我想你⼼⾥⾃然会有答案。 下⾯就按照顺序讲解如上 7 种智能指针(smart_ptr)。 ⼆、具体使⽤ 1、总括 对于编译器来说,智能指针实际上是⼀个栈对象,并⾮指针类型,在栈对象⽣命期即将结束时,智能指针通过析构函数释放有它管理的 堆内存。所有智能指针都重载了"operator->"操作符,直接返回对象的引⽤,⽤以操作对象。访问智能指针原来的⽅法则使⽤"."操作符。 访问智能指针包含的裸指针则可以⽤ get() 函数。由于智能指针是⼀个对象,所以if (my_smart_object)永远为真,要判断智能指针的裸指 针是否为空,需要这样判断:if (my_smart_object.get())。 智能指针包含了 reset() ⽅法,如果不传递参数(或者传递 NULL),则智能指针会释放当前管理的内存。如果传递⼀个对象,则智能指 针会释放当前对象,来管理新传⼊的对象。 我们编写⼀个测试类来辅助分析: class Simple { public: Simple(int param = 0) { number = param; std::cout << "Simple: " << number << std::endl; } ~Simple() { std::cout << "~Simple: " << number << std::endl; } void PrintSomething() { std::cout << "PrintSomething: " << info_extend.c_str() << std::endl; } std::string info_extend; int number; }; 2、std::auto_ptr std::auto_ptr 属于 STL,当然在 namespace std 中,包含头⽂件 #include<memory> 便可以使⽤。std::auto_ptr 能够⽅便的管理单个堆 内存对象。 我们从代码开始分析: void TestAutoPtr() { std::auto_ptr<Simple> my_memory(new Simple(1)); // 创建对象,输出:Simple:1 if (my_memory.get()) { // 判断智能指针是否为空 my_memory->PrintSomething(); // 使⽤ operator-> 调⽤智能指针对象中的函数 my_memory.get()->info_extend = "Addition";
C++智能指针是一种用于管理动态分配的内存资源的工具。C++中提供了多种类型的智能指针,其中包括shared_ptr、unique_ptr和weak_ptr。这些智能指针都位于头文件<memory>中。 其中,shared_ptr是一种引用计数智能指针,它允许多个智能指针共享同一个对象。shared_ptr通过对对象的引用计数来管理内存的释放,当引用计数为0时,即没有智能指针指向该对象时,对象会被自动释放。使用shared_ptr需要包含头文件<memory>,并通过new关键字创建动态分配的对象并将其交给shared_ptr进行管理。 另一种智能指针是unique_ptr,它是一种独占型智能指针,只能有一个智能指针拥有对对象的所有权。当unique_ptr对象被销毁时,它所管理的对象也会被自动释放。unique_ptr提供了更高效的内存管理方式,因为它不需要进行引用计数。使用unique_ptr也需要包含头文件<memory>,并使用new关键字创建动态分配的对象并将其交给unique_ptr进行管理。 除了shared_ptr和unique_ptr,还有其他类型的智能指针,如weak_ptr和scoped_ptr。weak_ptr是一种弱引用智能指针,它用于解决shared_ptr可能出现的循环引用问题。scoped_ptr是一种简单的智能指针,它只能在创建时初始化,并且不能进行复制和赋值操作。scoped_ptr在其所属的作用域结束时自动释放所管理的对象。 总结起来,C++智能指针是一种用于管理动态分配的内存资源的工具,包括shared_ptr、unique_ptr、weak_ptr和scoped_ptr等类型。它们提供了一种更安全、更高效的内存管理方式,避免了手动释放内存的麻烦。要使用这些智能指针,需要包含头文件<memory>,并通过new关键字创建动态分配的对象并将其交给相应的智能指针进行管理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [C++ -- 智能指针( C++11与boost库的智能指针及其使用)](https://blog.csdn.net/xu1105775448/article/details/80625936)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [C++智能指针的底层实现原理](https://blog.csdn.net/ArtAndLife/article/details/120793343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值