在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
最近工作中用到了句柄类,特意了解了一下,后来发现所谓的句柄其实和智能指针就是一回事。为了能够更好的理解句柄类,建议大家先了解c++中类、类的继承、多态等概念,不然很容易懵。 什么是句柄类句柄(handler)之所以翻译成柄,就是用一个类来撬动很多类。类似于从一大堆数中,只要拿着一个数(基类),后面跟着很多数(继承类)也都顺带被拿起来了。 句柄的实现主要实现了三个作用:
为什么要引入句柄类先来看一个例子,此处参考神奇爱哥的博文。 #include<iostream> #include<vector> using namespace std; class A { public: A(){} ~A(){} virtual void func() { cout<<"A"<<endl;} }; class B: public A { public: B(){} ~B(){} void func() { cout<<"B"<<endl;} }; int main() { A a; B b; vector<A> vec; vec.push_back(a); vec.push_back(b); vec[0].func(); vec[1].func(); } 最后的输出是: 因为对象b的拷贝在vector中时被强制转换成基类了。 要在一个容器中放基类及其继承类的时候总会遇到这个问题,因为vector的类型只能被声明为某一个类(假设为基类),那么继承类的信息就丢失了。 首先可以想到用指针来实现多态,例如vector<A*>。但是这样必须要程序员来接管内存。 B *b = new B; vec.push_back(b); 这时加入vec的生命周期结束了,vec不会主动释放b所占用的内存,如果不手动deleteb,那么就会造成内存泄漏。 而句柄类可以解决这个问题。 #include<iostream> #include<vector> using namespace std; class A { public: A(){} ~A(){} virtual void func() const { cout<<"A"<<endl;} virtual A* clone() const //为了实现句柄类新增的函数 { return new A(*this);} }; class B: public A { public: B(){} ~B(){} void func() const { cout<<"B"<<endl;} virtual B* clone() const //为了实现句柄类新增的函数 { return new B(*this);} }; class sample { public: sample(): p(0),use(1){} sample(const A& a):p(a.clone()){use++;} //引用构造 sample(const sample& i):p(i.p),use(i.use){use++;} //引用构造 ~sample() {decr_use();} sample& operator=(const sample &i) { use++; decr_use(); p = i.p; use = i.use; return (*this); } const A* operator->() const {return p;} const A& operator*() const {return *p;} private: A* p; size_t use; void decr_use(){if(--use==0) delete p;} }; int main() { vector<sample> vec; //sample类看起来是对象,但是其实用法和指针一样。 A a; B b; sample s1(a); //将a拷贝构造了一个新对象,并让s1指向这个新的对象。此时s1已经与a无关了。 sample s2(b); //将b拷贝构造了一个新对象,并让s2指向这个新的对象。此时s2已经与b无关了。 vec.push_back(s1); vec.push_back(s2); vec[0]->func(); vec[1]->func(); } 此时运行结果为: 因此句柄类方便得实现了多态,并且和智能指针一样可以自动管理内存。 上面的代码需要注意为什么克隆函数里要用 virtual A* clone() const {return new A(*this);} 而不是直接virtual A* clones() const(return this;} 这是为了避免这样一种情况: B b; sample sam(b); vec.push_back(sam); 当b的生命周期结束,而vec的生命周期未结束时,调用(*iter)->func就会出错。 句柄类的作用从上面的例子中可以看出,句柄类sample的实现和它所管理的类A的实现时完全独立的。因此即使更改了A的功能,句柄类也依然可以保持不变。 句柄类本质是指针!!在使用句柄类需要特别注意的一点是,操作句柄类时本质就是在操作指针,有时解引用了非const的函数可能就会直接改变其真正指向的对象。 来看一个例子。 #include<iostream> #include<vector> using namespace std; class A { public: A(){numA = 0;} ~A(){} virtual void func() const { cout<<"A"<<endl;} virtual A* clone() const //为了实现句柄类新增的函数 { return new A(*this);} void setNum(int n) //增加了一个设置num属性的函数 { numA = n;} int getNum() const { return numA;} private: int numA; }; class B: public A { public: B(){numB = 0;} ~B(){} void func() const { cout<<"B"<<endl;} virtual B* clone() const //为了实现句柄类新增的函数 { return new B(*this);} void setNum(int n) //增加了一个设置num属性的函数 { numB = n;} int getNum() const { return numB;} private: int numB; }; class sample { public: sample(): p(0),use(1){} sample(const A& a):p(a.clone()){use++;} //引用构造 sample(const sample& i):p(i.p),use(i.use){use++;} //引用构造 ~sample() {decr_use();} sample& operator=(const sample &i) { use++; decr_use(); p = i.p; use = i.use; return (*this); } A* operator->() const {return p;} A& operator*() const {return *p;} private: A* p; size_t use; void decr_use(){if(--use==0) delete p;} }; void changeA(sample sam) { sam->setNum(6); } int main() { A a; sample s1(a); cout<<"s1中num的值: "<<s1->getNum()<<endl; cout<<"a的num值: "<<a.getNum()<<endl; changeA(s1); cout<<"更改后s1中num的值: "<<s1->getNum()<<endl; cout<<"更改后a的num值: "<<a.getNum()<<endl; } 在调用changeA时,注意并非引用调用,而是直接把sample对象传递进来。 输出结果为: 可以看出,changeA的形参并没有显示指定是指针,也不是引用,但是却真实得改变了s1指向对象的值。 由于s1(a)时执行了构造函数构造了新的对象,因此s1与a其实无关,所以a的值不变。 所以在使用句柄类时需要牢记自己操作的是指针,虽然看起来像对象,但是本质是指针。
参考: https://blog.csdn.net/xgf415/article/details/52962875 https://blog.csdn.net/qingcaichongchong/article/details/7559801
|
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论