C++ STL Smart Pointer 智能指针(二)unique_ptr
unique_ptr实现了独占式拥有概念,它可以保证一个对象和其相应的资源同一时间只被一个pointer拥有。一旦拥有者被销毁或变成empty,或开始拥有另一个对象,先前拥有的那个对象就会被销毁,其任何相应的资源亦会被释放。
1.使用unique_ptr
#include <iostream> #include <string> #include <memory> using namespace std; int main(){ unique_ptr<string> up(new string("nico")); (*up)[0]='N'; up->append("lai"); cout<<*up<<endl; }
输出:
Nicolai
2.转移unique_ptr的拥有权
以下的代码是错误的:
std::string* sp=new std::string("hello"); std::unique_ptr<std::string> up1(sp); std::unique_ptr<std::string> up2(sp);
up1和up2拥有了同样的对象。
unique_ptr<A> up1(new A("a1")); unique_ptr<A> up2(up1); //Error:up1 and up2 own same data
这里的up2不可以成为原对象的另一个拥有者。
正确的转移方法:
unique_ptr<A> up1(new A("a1")); unique_ptr<A> up2(move(up1)); unique_ptr<A> up3; up3=move(up2);
3.被当作成员
a.h
#ifndef A_H #define A_H #include <iostream> #include <string> using namespace std; class A { public: A(string n); A(int n); ~A(); int a=0; string s="a"; }; #endif // A_H
a.cpp
#include "a.h" A::A(string n) { s=n; } A::A(int n){ a=n; } A::~A(){ cout<<"delete "<<s<<endl; }
b.h
#ifndef B_H #define B_H #include <iostream> #include "a.h" #include <string> #include <memory> using namespace std; class B { public: B(int val1,int val2); B(const B& x); B& operator =(const B& x); void print(); private: unique_ptr<A> ptr1; unique_ptr<A> ptr2; }; #endif // B_H
b.cpp
#include "b.h" B::B(int val1,int val2):ptr1(new A(val1)),ptr2(new A(val2)) { } B::B(const B &x):ptr1(new A(*x.ptr1)),ptr2(new A(*x.ptr2)){ } B& B::operator =(const B& x){ *ptr1=*x.ptr1; *ptr2=*x.ptr2; return *this; } void B::print(){ cout<<"p1: "<<ptr1->a<<" p2: "<<ptr2->a<<endl; }
main函数
int main(){ B b(10,1); b.print(); }
输出:
p1: 10 p2: 1
delete a
delete a
对于B,你可以不写析构函数,因为unique_ptr会为你做了该做的事。你还必须写出copy构造函数和赋值操作符;此二者的默认版本会拷贝复制成员,而在此那是不可能的。如果你没有自行提供它们,Class B将只能提供move语义。
4.对付Array
基于C语言以来的规定,C++无法区分指针是“指向单对象”还是“指向array”。C++规定,对于数组应该使用delete[]而不是delete。所以以下语句是错误的:
std::unique_ptr<std::string> up(new std::string[10]);
C++标准库为unique_ptr提供了一个偏特化的版本用来处理array,这个版本会在遗失其所指对象的拥有权时,对该对象调用delete[]。使用方法如下:
int main(){ unique_ptr<string[]> up6(new string[10]); up6[0]="aaa"; //cout<<*up<<endl;//Error cout<<"up6[0]:"<<up6[0]<<endl; }
然而,这个版本的接口稍有不同。它不再提供操作符*和->,而改提供操作符[]。
这个class不接受派生类型的array作为初值,因此起不到多态的作用。