C++回顾(三)智能指针

来源:互联网 发布:淘宝男装夏季 编辑:程序博客网 时间:2024/05/18 03:44

1、关键字explicit

什么是隐式转换?

#include<iostream>using namespace std;class Fuck{private:int _value;public:Fuck(int value){_value = value;}void showFuck(){cout << _value << endl;}};int main(){Fuck f = 5;f.showFuck();int a;cin >> a;}
运行结果:5

其中

Fuck f = 5;
就是隐式转换

如何禁止这种隐式转换?

#include<iostream>using namespace std;class Fuck{private:int _value;public:explicit Fuck(int value){_value = value;}void showFuck(){cout << _value << endl;}};int main(){Fuck f = 5;f.showFuck();int a;cin >> a;}
编译不通过

1>------ 已启动生成:  项目: Project1, 配置: Debug Win32 ------1>  Test.cpp1>c:\users\yu yong\desktop\test\project1\project1\test.cpp(18): error C2440: “初始化”: 无法从“int”转换为“Fuck”1>          class“Fuck”的构造函数声明为“explicit”========== 生成:  成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========

2、异常机制

throw抛出异常,以及try/catch捕获异常

#include<iostream>#include<cmath>using namespace std;int main(){try{throw exception("std fuck");throw "fuck";}catch (exception &exp)//用于捕捉标准异常{cout << exp.what() << endl;}catch (...)//用于捕捉任意类型的异常{cout << "fuck all error" << endl;}int t;cin >> t;}

关于可变参数

#include<iostream>#include<cmath>using namespace std;void fuckStr(int paramsNum, ...){for (int i = 0; i < paramsNum; i++){string* ptr = (string*)(¶msNum + i);cout << " " << ptr->c_str();}cout << endl;}void fuckInt(int paramsNum, ...){for (int i = 0; i < paramsNum; i++){int* ptr = (int*)(¶msNum + i + 1);cout << " " << *ptr;}cout << endl;}int main(){fuckInt(3, 111, 333, 555);fuckStr(3, "111", "333", "555");int a;cin >> a;}
运行结果:
 111 333 555
 111 333 555

关于throw()

举例及解释如下:
void fun() throw() 表示fun不允许抛出任何异常,即fun是异常安全的。搜索
void fun() throw(...) 表示fun可以抛出任何形式的异常。
void fun() throw(exceptionType) 表示fun只能抛出exceptionType类型的异常。
比如:
void GetTag() throw(int); 表示只抛出int类型异常
void GetTag() throw(int,char); 表示抛出in,char类型异常
void GetTag() throw(); 表示不会抛出任何类型异常
void GetTag() throw(...); 表示抛出任何类型异常
那么,void GetTag() throw(int); 表示只抛出int类型异常  这句解释怎么理解呢?并不表示一定会抛出异常,但是一旦抛出异常只会抛出int类型。如果抛出非int类型异常,调用unexsetpion()函数,退出程序。

3、智能指针

auto_ptr,unique_ptr,shared_ptr,weak_ptr

其中auto_ptr是在C++98标准中支持的。

unique,shared_ptr,weak_ptr是在C++11中支持的,另外C++11已经摒弃了auto_ptr。

什么时候需要智能指针?

例如出现异常的时候(虽然我们可以在抛出异常之前调用delete,但是这样做毕竟不是很灵活)

#include<iostream>#include<cmath>using namespace std;int main(){string * test = new string("aaa");if (1 == 1){throw exception("fuck aaa");}delete test;}

一旦抛出异常,这个test指向的内存就会泄漏

如何应对?

如上面的例子。在main函数退出(无论是异常还是正常)时,test指针本身的内存会被释放,但是test指针指向的内存空间却不一定。

如果test指针不是基本的指针变量而是类变量,那么在test指针本身失效的时候就一定会调用析构函数。这样就可以在析构函数里考虑是否释放所指空间了。

智能指针的基本原理就是在指针本身失效(被析构)的时候,检测其指向的内存空间是否继续被需要(这通常通过维护一个计数器来实现),如果不是则释放。

由此可知析构过程和计数器是设计智能指针的关键!


auto_ptr

#include<iostream>#include<memory>using namespace std;class Fuck{public:Fuck(){cout << "fuck is created" << endl;}~Fuck(){cout << "fuck is destroyed" << endl;}void SayFuck(){cout << "FUCK" << endl;}};int main(){{auto_ptr<Fuck> p_fuck(new Fuck());p_fuck->SayFuck();}int a;cin >> a;}


结果

fuck is created
FUCK
fuck is destroyed


其他的智能指针的使用方式也是类似的,总结如下:

1、包含头文件memory

2、将原本的指针包装在智能指针对象内

3、删除原本的delete语句


所有智能指针都应该避免的用法:

#include<iostream>#include<memory>using namespace std;class Fuck{public:Fuck(){cout << "fuck is created" << endl;}~Fuck(){cout << "fuck is destroyed" << endl;}void SayFuck(){cout << "FUCK" << endl;}};int main(){{//auto_ptr<Fuck> p_fuck(new Fuck());Fuck f;auto_ptr<Fuck> p_fuck(&f);p_fuck->SayFuck();}int a;cin >> a;}

运行结果:

出现异常


这时,f变量分配的是栈内存而不是堆内存,在p_fuck失效的时候试图将delete作用域非堆内存将会引发错误。

#include<iostream>#include<memory>using namespace std;class Fuck{public:Fuck(){cout << "fuck is created" << endl;}~Fuck(){cout << "fuck is destroyed" << endl;}void SayFuck(){cout << "FUCK" << endl;}};int main(){{Fuck f;Fuck* p_f = &f;delete(p_f);}int a;cin >> a;}

运行结果:

出现异常



aotu_ptr弊端

#include<iostream>#include<memory>using namespace std;class Fuck{public:Fuck(){cout << "fuck is created" << endl;}~Fuck(){cout << "fuck is destroyed" << endl;}void SayFuck(){cout << "FUCK" << endl;}};int main(){{auto_ptr<Fuck> p_fuck(new Fuck());auto_ptr<Fuck> p1_fuck = p_fuck;}cout << "finish" << endl;int a;cin >> a;}

此时会发生所谓的所有权转让,即当autp_ptr对象相互赋值的时候,右值所持有指针的多有权将会转让给左值,右值将不再持有这个指针。这点存在潜在的问题,即在发生所有权转让之后,再次调用右值将会导致错误

#include<iostream>#include<memory>using namespace std;class Fuck{private:string name;public:Fuck(string _name){name = _name;cout << "fuck is created" << endl;}~Fuck(){cout << "fuck is destroyed" << endl;}void SayFuck(){cout << "FUCK" << name.c_str() << endl;}};int main(){{auto_ptr<Fuck> p1(new Fuck("f1"));auto_ptr<Fuck> p2 = p1;p2->SayFuck();p1->SayFuck();}int a;cin >> a;}
这段代码在执行p1->SayFuck();时会出现异常,因为所有权在此前已经转让给了p2,p1再次对指针进行操作的时候将会出现空指针异常。在某些情况下(如数组遍历)这种错误很严重!


#include<iostream>#include<memory>using namespace std;class Fuck{private:string name;public:Fuck(string _name){name = _name;cout << "fuck is created" << endl;}~Fuck(){cout << "fuck is destroyed" << endl;}void SayFuck(){cout << "FUCK" << name.c_str() << endl;}};int main(){auto_ptr<Fuck> p_fuck_s[] = { auto_ptr<Fuck>(new Fuck("f1")), auto_ptr<Fuck>(new Fuck("f2")), auto_ptr<Fuck>(new Fuck("f3")), auto_ptr<Fuck>(new Fuck("f4")) };for (int i = 0; i < 4; i++){auto_ptr<Fuck> f_this = p_fuck_s[i];f_this->SayFuck();}for (int i = 0; i < 4; i++){auto_ptr<Fuck> f_this = p_fuck_s[i];f_this->SayFuck();}int a;cin >> a;}


运行结果:

fuck is created
fuck is created
fuck is created
fuck is created
FUCKf1
fuck is destroyed
FUCKf2
fuck is destroyed
FUCKf3
fuck is destroyed
FUCKf4
fuck is destroyed

出现异常


第二次for出现异常,因为第一次for的时候数组内的智能指针已经失去了所有权,并且局部变量f_this在每一次失效的时候都会将实际指向的内存释放。解决这种问题,需要允许多个智能指针共享所有权,同时在智能指针内部维护一个计数器用于计数引用次数,当引用降低到0时再实际执行释放。这就是shred_ptr的基本设计思想

shared_ptr

#include<iostream>#include<memory>using namespace std;class Fuck{private:string name;public:Fuck(string _name){name = _name;cout << "fuck is created" << endl;}~Fuck(){cout << "fuck is destroyed" << endl;}void SayFuck(){cout << "FUCK" << name.c_str() << endl;}};int main(){{shared_ptr<Fuck> p_fuck_s[] = { shared_ptr<Fuck>(new Fuck("f1")), shared_ptr<Fuck>(new Fuck("f2")), shared_ptr<Fuck>(new Fuck("f3")), shared_ptr<Fuck>(new Fuck("f4")) };for (int i = 0; i < 4; i++){shared_ptr<Fuck> f_this = p_fuck_s[i];f_this->SayFuck();}for (int i = 0; i < 4; i++){shared_ptr<Fuck> f_this = p_fuck_s[i];f_this->SayFuck();}}int a;cin >> a;}


运行结果

fuck is created
fuck is created
fuck is created
fuck is created
FUCKf1
FUCKf2
FUCKf3
FUCKf4
FUCKf1
FUCKf2
FUCKf3
FUCKf4
fuck is destroyed
fuck is destroyed
fuck is destroyed
fuck is destroyed


unique_ptr

这种智能指针也可用于解决所有权转移的问题的,但是策略就比较简单粗暴,直接不允许所有权转移!

unique_ptr<Fuck> p1(new Fuck("f1"));unique_ptr<Fuck> p2 = p1;

这样的复制语句直接编译不过。