为什么需要auto_ptr_ref
分类:web前端

转载自:

     shared_ptr是一个最像指针的“智能指针”,是boost.smart_ptr库中最有价值、最要紧的组成部分,也是最有效的。

在代码里面见到了auto_ptr那么些事物,正巧从前一弟兄曾经问过作者那些主题材料..所以特意去搜了搜帖子,学习学习

     scoped_ptr不许拷贝、赋值(因为scoped_ptr同一时间把拷贝布局函数和赋值操作符都宣称为个人的,防止对智能指针的复制操作),只可以在scoped_ptr被声称的作用域使用。除了*和->外也远非概念其余的操作符(不能对scoped_ptr进行++可能--等指针算术操作)。

谢谢原著者,自个儿也被苦闷了整多少个晚间。

     shared_ptr实现的是引用计数的智能指针,能够被轻巧拷贝和赋值,在自由之处分享它,当未有代码应用(引用计数为0)它时才删除被装进的动态分配的靶子。shared_ptr也足以安全地置于规范容器中。

演示如下:

+++++++++++++++++++++++++++++++++++

示例1:

 

#include <boost/smart_ptr.hpp>

前段时间初步拜读侯捷先生和孟岩先生的译作《C++标准程序库:自修教程与参照他事他说加以考察手册》。两位先生真正译功上乘,读得很顺。不过读到P55页关于auto_ptr_ref的座谈,却百思不解:为啥需求引进auto_ptr_ref那些扶植类呢?

#include <boost/smart_ptr.hpp>
#include <iostream>
#include <cassert>
using namespace boost;
using namespace std;
int main()
{
shared_ptr<int> sp(new int(10卡塔尔国State of Qatar;      //一个针对整数的shared_ptr
assert(sp.unique());                 //现在shared_ptr是指针的独一全体者
shared_ptr<int> sp2 = sp;             //第二个shared_ptr,拷贝构造函数
assert(sp == sp2 && sp.use_count() == 2);
*sp2 = 100;                 //使用解援引操作符校正被指指标
assert(*sp == 100);
sp.reset();                 //停止shared_ptr的引用
assert(!sp卡塔尔;                //sp不再抱有此外指针(空指针)
return 0;
}
示例2:

头文件   :   #include <memory>

#include <iostream>
#include <string>
using namespace boost;
using namespace std;
int main()
{
typedef scoped_ptr<string> s_ptr;
s_ptr sp(new string("text"));
cout << *sp << endl;
cout << sp->size() << endl;

从书中描述来看,就如与拷贝布局函数、右值、类型转变有关。于是,结合auto_ptr的源代码,google之、baidu之,找了一推资料,终于初阶搞清该问题。

#include <boost/smart_ptr.hpp>
#include <iostream>
#include <cassert>

使用    :  std::auto_ptr

    scoped_array与scoped_ptr源于相近的陈设性思想,故而用法极其相同。

auto_ptr的具有权

using namespace boost;
using namespace std;

作用    :  动态分配对象以及当对象不再需要时自动执行清理(意思就是你不用再去关心什么时候delete了也不用担心发生异常会有内存泄漏)

示举例下 :

C++司空见惯的智能指针有std::auto_ptr、boost::shared_ptr、boost::scoped_ptr、boost::shared_array、boost::scoped_array等。auto_ptr只是此中一种而已。可是,为啥auto_ptr才有auto_ptr_ref,而boost::shared_ptr却没有shared_ptr_ref呢?

class shared             //一个装有shared_ptr的类
{
private:
shared_ptr<int> p;
public:
shared(shared_ptr<int> p_) : p(p_State of Qatar { }   //构造函数初阶化shared_ptr
void print()          //输出shared_ptr的引用计数和指向的值
{
cout << "count: " << p.use_count()
<< " v = " << *p << endl;
}
};

实现    :  在C++中, auto_ptr是一个类,它用来实现对动态分配对象的自动释放。

#include <boost/smart_ptr.hpp>
#include <iostream>
#include <string>
using namespace boost;
using namespace std;
int main()
{
int *arr = new int[100];
scoped_array<int> sa(arr);
fill_n(&sa[0], 100, 5);
sa[10] = sa[20] + sa[30];
cout << sa[10] << endl;
return 0;
}

答案与auto_ptr的表征有关。auto_ptr重申对财富的拥有权(ownership)。也正是说,auto_ptr是"它所指对象"的具备者。而三个目的只好归于三个具有者,严禁一物二主,否则正是重婚罪,意料外的灾祸将惠临。

void print_func(shared_ptr<int> p)
{
//相符输出shared_ptr的引用计数和针对性的值
cout << "count: " << p.use_count()
<< " v = " << *p << endl;
}

智能指针代码剖判:

return 0;
}

为了保险auto_ptr的具备权独一,auto_ptr的正片构造函数和赋值操作符做了那般一件事情:移除另八个auto_ptr的具有权。为了验证具备权的转变,请看上边的代码示例:

int main()
{
shared_ptr<int> p(new int(110));
shared s1(pState of Qatar, s2(p卡塔尔;       //构造几个自定义的类

  1 template<class T>      //模板类
  2 class auto_ptr
  3 {
  4 private:
  5     T*ap;        //int* ap;   char* ap;   father* ap;  child* ap;
  6 public:
  7     //constructor & destructor-----------------------------------(1)
  8     explicit auto_ptr(T*ptr=0)throw():ap(ptr)//explicit->防止隐式转换(隐式转换只会在构造函数只有一个参数的时候发生)
  9     {
 10     }
 11  
 12     ~auto_ptr()throw()   //直接删除ap.如果ap是null也没关系,c++不会认为错误
 13     {
 14         delete ap;
 15     }
 16     //Copy & assignment--------------------------------------------(2)
 17     auto_ptr(auto_ptr& rhs)throw():ap(rhs.release())  //拷贝构造函数
 18     {
 19     }
 20 
 21     template<class Y>  //模板拷贝构造函数-->重载的拷贝构造函数
 22     auto_ptr(auto_ptr<Y>&rhs)throw():ap(rhs.release())
 23     {
 24     }
 25 
 26     auto_ptr& operator=(auto_ptr&rhs)throw() //重载的赋值操作符
 27     {
 28         reset(rhs.release());
 29         return*this;
 30     }
 31 
 32     template<class Y>  //模板重载的赋值操作费
 33     auto_ptr& operator=(auto_ptr<Y>&rhs)throw()
 34     {
 35         reset(rhs.release());
 36         return*this;
 37     }
 38 
 39     //Dereference----------------------------------------------------(3)
 40     T& operator*()const throw()   //重载*  注意返回的是类的引用
 41     {
 42         return *ap;
 43     }
 44     T* operator->()const throw()  //重载-> 
 45     {
 46         return ap;
 47     }
 48 
 49     //Helper functions------------------------------------------------(4)
 50     //value access
 51     T* get()const throw()   
 52     {
 53         return ap;
 54     }
 55 
 56     //release owner ship
 57     T* release()throw()   //此函数是在   返回临时对象???????????
 58     {
 59         T*tmp(ap);  //调用构造函数tmp.ap 执行this->ap
 60         ap=0;
 61         return tmp;
 62     }
 63 
 64     //reset value
 65     void reset(T*ptr=0)throw()
 66     {
 67         if(ap!=ptr)
 68         {
 69             delete ap;
 70             ap=ptr;
 71         }
 72     }
 73 
 74     //Special conversions-----------------------------------------------(5)  //后面的真心看不懂啊
 75     template<class Y>
 76     struct auto_ptr_ref
 77     {
 78         Y* yp;
 79         auto_ptr_ref(Y* rhs) : yp(rhs){}
 80     };
 81     auto_ptr(auto_ptr_ref<T>rhs)throw():ap(rhs.yp)    //辅助的拷贝构造函数
 82     {
 83     }
 84      
 85     auto_ptr& operator=(auto_ptr_ref<T>rhs)throw()
 86     {
 87         reset(rhs.yp);
 88         return*this;
 89     }
 90      
 91     template<class Y>
 92     operator auto_ptr_ref<Y>()throw()
 93     {
 94         return auto_ptr_ref<Y>(release());
 95     }
 96      
 97     template<class Y>
 98     operator auto_ptr<Y>()throw()
 99     {
100         return auto_ptr<Y>(release());
101     }
102 };

#include <iostream>
#include <memory>
using namespace std;

s1.print();
s2.print();

1 构造函数与析构函数

int main(int argc, char **argv){
    auto_ptr<int> ptr1(new int(1));
    auto_ptr<int> ptr2(ptr1卡塔尔国;    //ptr1的具有权被转换成ptr2

*p = 20;           //修改shared_ptr所指的值
print_func(p);

auto_ptr在组织时得到对某些对象的全数权(ownership卡塔尔国,在析构时释放该指标。我们可以如此使用auto_ptr来升高代码安全性:

    auto_ptr<int> ptr3(NULL);
    ptr3 = ptr2;                 //ptr2的具有权被撤换来ptr3

s1.print();

1
2
int*p=new int(0);
auto_ptr<int> ap(p);

    cout<<ptr1.get()<<endl;     //结果为0
    cout<<ptr2.get()<<endl;     //结果为0
    cout<<*ptr3<<endl;          //结果为1

return 0;
}
在宣称了shared_ptr和四个shared实例后,指针被它们所分享,因而援引计数为3.printf_func函数内部拷贝了一个shared_ptr对象,由此引用计数再追加1,但当退出函数时拷贝自动析构,援用计数又大张旗鼓3。

然后大家不要关心应该什么时候释放p,也不用忧郁产生分外会有内部存款和储蓄器泄漏。

auto_ptr的正片布局函数与赋值操作符  

程序运转结果如下:

注意事项:

由于要求贯彻具备权的调换,auto_ptr的正片构造函数和赋值操作符,与常常类的做法不太周边。我们得以看看MinGW 5.1.6得以达成的auto_ptr源代码:

 count:3 v = 110

1).auto_ptr析构的时候明确会删除他所具备的不得了指标,所以大家将要用心了,一点露水一棵葱,八个auto_ptr不可能同时负有同多个目的。

/**
*  @brief  An %auto_ptr can be constructed from another %auto_ptr.
*  @param  a  Another %auto_ptr of the same type.
*
*  This object now @e owns the object previously owned by @a a,
*  which has given up ownsership.
*/
auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) {}

count:3 v = 110

1 int* p = new int(0);
2 auto_ptr<int> ap1(p);
3 auto_ptr<int> ap2(p); //错误,一个p给了两个智能指针

/**
*  @brief  %auto_ptr assignment operator.
*  @param  a  Another %auto_ptr of the same type.
*
*  This object now @e owns the object previously owned by @a a,
*  which has given up ownsership.  The object that this one @e
*  used to own and track has been deleted.
*/
auto_ptr&
operator=(auto_ptr& __a) throw () {
    reset(__a.release());
    return *this;
}

count:4 v = 20

2).auto_ptr的析构函数中剔除指针用的是delete,并不是delete [],所以大家不应有用auto_ptr来管理三个数组指针

能够见到,auto_ptr的正片布局函数、赋值操作符,它们的参数都以auto_ptr&,而不是auto_ptr const &。

count:3 v = 20

1 int* pa = new int[10];
2 auto_ptr<int> ap(pa); //错误,在delete的时候只会删除数组的第一个元素,其他元素申请的空间不会被删除

    日常的话,类的正片布局函数和赋值操作符的参数都是const &。但是auto_ptr的做法也是合理合法的:管教具备权能够转移

3). 构造函数的explicit关键词有效阻止从二个“裸”指针隐式调换来auto_ptr类型

    如果auto_ptr的正片构造函数和赋值操作符的参数是auto_ptr const &,那么实参的具有权将不能够退换。因为更改具有权需求校正auto_ptr的成员变量,而实参确是多个const对象,不容许改进。

4.卡塔尔(قطر‎因为C++有限援救删除一个空指针是安全的, 所以大家从没供给把析构函数写成:

右值与const &

1
2
3
4
~auto_ptr()throw()
{
    if(ap)delete ap;
}

一经我们想写出上面包车型地铁代码:

2 拷贝布局与赋值

#include <memory>
using namespace std;

与引用计数型智能指针不同的,auto_ptr供给其对“裸”指针的完全占领性。相当于说一个“裸”指针无法同期被八个以上的auto_ptr所全体。那么,在拷贝布局或赋值操作时,我们必需作非常的拍卖来保险那性子格。auto_ptr的做法是“全体权转移”,即拷贝或赋值的源对象将遗失对“裸”指针的全数权,所以,与平日拷贝布局函数,赋值函数不同, auto_ptr的正片构造函数,赋值函数的参数为援引并非常引用(const reference卡塔尔.当然,四个auto_ptr也不能够何而且有三个以上的“裸”指针,所以,拷贝或赋值的靶子对象将先放出其本来所持有的对象。

int main(int argc, char **argv) {
    auto_ptr<int> ptr1(auto_ptr<int>(new int(1卡塔尔(قطر‎State of Qatar卡塔尔;  //使用不时对象开展拷贝布局
    auto_ptr<int> ptr2(NULL);
    ptr2 = (auto_ptr<int>(new int(2State of Qatar卡塔尔(قطر‎卡塔尔(قطر‎;  //使用不常对象开展赋值
}

此地的注意点是:

      借使未有定义auto_ptr_ref类及有关的函数,那么这段代码将不能够经过编写翻译。主要的缘故是,拷贝布局函数及赋值操作符的参数:auto_ptr<int>(new int(1))和auto_ptr<int>(new int(2))都是临时对象。一时对象归属规范的右值,而非const &是不能够指向右值的(参见More Effective C++,Item 19)。auto_ptr的正片布局函数及赋值操作符的参数类型恰好是auto_ptr&,明显 非const &。

1卡塔尔因为多少个auto_ptr被拷贝或被赋值后, 其曾经失却对原对象的全部权,这时候,对这些auto_ptr的提领(dereference卡塔尔操作是不安全的。如下:

同理,上面包车型大巴两段代码,也不会透过编写翻译:

1
2
3
4
int*p=new int(0);
auto_ptr<int>ap1(p);
auto_ptr<int>ap2=ap1;
cout<<*ap1;//错误,此时ap1只剩一个null指针在手了

#include <iostream>
#include <memory>
using namespace std;
auto_ptr<int> f();
int main(int argc, char **argv) {
    auto_ptr<int> ptr3(f()State of Qatar;  //使用不经常对象实行拷贝布局
    auto_ptr<int> ptr4(NULL);
    ptr4 = f(State of Qatar;          //使用有时对象进行赋值
}

这种景况比较隐讳的情况出今后将auto_ptr作为函数参数按值传递,因为在函数调用经过中在函数的作用域中会产生八个有的对象来收纳传入的auto_ptr(拷贝构造卡塔尔,那样,传入的实参auto_ptr就错失了其对原对象的全部权,而该对象会在函数退出时被部分auto_ptr删除。如下:

#include <iostream>
#include <memory>
using namespace std;
auto_ptr<int> f(){
    return auto_ptr<int>(new int(3State of Qatar卡塔尔国;  //这里实在也应用不经常对象实行拷贝布局
}

1
2
3
4
5
6
7
8
void f(auto_ptr<int>ap)
{
    cout<<*ap;
}
 
auto_ptr<int>ap1(new int(0));
f(ap1);
cout<<*ap1;//错误,经过f(ap1)函数调用,ap1已经不再拥有任何对象了。

何奇之有类不会高出这一个主题素材,是因为他俩的正片结构函数及赋值操作符(不管是客户定义依旧编写翻译器生成的版本),参数都以const &。

因为这种意况太隐瞒,太轻巧失误了, 所以auto_ptr作为函数参数按值传递是必然要防止的。也许大家会想到用auto_ptr的指针或引用作为函数参数或者能够,可是留神思谋,大家并不知道在函数中对传播的auto_ptr做了怎么样, 假诺当中某个操作使其失去了对指标的全体权, 那么那依然唯恐会变成致命的推行期错误。 可能,用const reference的情势来传递auto_ptr会是三个没有错的选项。

auto_ptr_ref之目的

2卡塔尔大家得以看见拷贝布局函数与赋值函数都提供了叁个成员模板在不蒙蔽“正统”版本的情况下促成auto_ptr的隐式转变。如我们有以下七个类

相传当年C++标准委员会的多数国度,因为这些标题都想把auto_ptr从规范库中去除。幸亏BillGibbons和格雷戈Colvin创制性地建议了auto_ptr_ref,搞定了这一标题,世界宁静了。

1
2
class base{};
class derived:public base{};

auto_ptr_ref之原理

那正是说下列代码就能够通过,完成从auto_ptr<derived>到auto_ptr<base>的隐式转变,因为derived*能够转变来base*类型

    很引人侧目,下边包车型客车布局函数,是能够选拔auto_ptr有的时候对象的。

1
auto_ptr<base>apbase=auto_ptr<derived>(new derived);

auto_ptr(auto_ptr __a) throw() : _M_ptr(__a.release()) { }

3) 因为auto_ptr不具有值语义(value semantic卡塔尔, 所以auto_ptr不能够被用在stl标准容器中。

但另叁个难点也很猛烈:上述布局函数不能够因此编写翻译。倘若能通过编译,就能陷于生生不息调用。

所谓值语义,是指契合以下标准的品类(要是有类A卡塔尔(قطر‎:

拷贝布局函数的参数类型必需是引用,

1
2
3
4
5
6
A a1;
A a2(a1);
A a3;
a3=a1;
//那么
a2==a1,a3==a1

缘由见链接:

很明显,auto_ptr不合乎上述原则,而作者辈精晓stl标准容器要用到大方的正片赋值操作,何况假如其操作的门类必须相符上述口径。

我们稍作修正:

3 提领操作(dereference卡塔尔(قطر‎

auto_ptr(auto_ptr_ref<element_type> __ref) throw()  //element_type就是auto_ptr的模版参数。
      : _M_ptr(__ref._M_ptr) { }

提领操作有五个操作, 一个是回去其所具有的对象的援引, 另四个则是兑现了经过auto_ptr调用其所负有的目的的分子。如:

该版本的布局函数,能够选用auto_ptr_ref的暂且对象。倘诺auto_ptr能够隐式调换来auto_ptr_ref,那么大家就能够用auto_ptr不时对象来调用该结构函数。那么些隐式转变轻易达成:

1
2
3
4
5
6
7
8
struct A
{
    void f();
}
 
auto_ptr<A>apa(new A);
(*apa).f();
apa->f();

template<typename _Tp1>
        operator auto_ptr_ref<_Tp1>() throw()
        { return auto_ptr_ref<_Tp1>(this->release()); }

当然, 大家第一要确定保证那一个智能指针当真具备某些对象,否则,那些操作的作为即对空指针的提领是未定义的

现今,我们得以写出上边包车型地铁代码,并得以通过编写翻译:

4 支持函数

#include <memory>
using namespace std;

1卡塔尔get用来显式的回来auto_ptr所持有的指标指针。大家能够开掘,标准库提供的auto_ptr既不提供从“裸”指针到auto_ptr的隐式调换(构造函数为explicit卡塔尔(قطر‎,也不提供从auto_ptr到“裸”指针的隐式调换,从利用上来说或者不那么的灵敏, 思谋到其所带给的安全性还是值得的。

int main(int argc, char **argv) {
    auto_ptr<int> ptr1(auto_ptr<int>(new int(1)));  //调用auto_ptr_ref版本的布局函数
}

2卡塔尔国 release,用来改变全体权

同理,要是大家再提供下边的函数:

3State of Qatar reset,用来选拔全体权,假如收到全数权的auto_ptr假如已经怀有某指标,必需先放出该对象。

auto_ptr&
      operator=(auto_ptr_ref<element_type> __ref) throw()
      {
    if (__ref._M_ptr != this->get())
      {
        delete _M_ptr;
        _M_ptr = __ref._M_ptr;
      }
    return *this;
      }

5 特殊调换

那么,上面包车型客车代码也能够透过编写翻译:

此处提供二个支持类auto_ptr_ref来做特别的更改,依照正规的解说, 那些类及上边4个函数的效果是:使我们能够拷贝和赋值non-const auto_ptrs, 却不可能拷贝和赋值const auto_ptrs. 作者不可能丰富标准的敞亮这两句话的意思,但基于大家观看与试验,应该能够如此去掌握:未有那一个代码,大家当然就能够拷贝和赋值non-const的auto_ptr和幸免拷贝和赋值const的auto_ptr的功能, 只是心有余而力不足拷贝和赋值临时的auto_ptr(右值卡塔尔国, 而那个帮扶代码提供一些转变,使大家得以拷贝和赋值有的时候的auto_ptr,但并从未使const的auto_ptr也能被拷贝和赋值。如下:

#include <iostream>
#include <memory>
using namespace std;

1
auto_ptr<int>ap1=auto_ptr<int>(new int(0));

int main(int argc, char **argv) {
    auto_ptr<int> ptr2(NULL);
    ptr2 = (auto_ptr<int>(new int(2)));  //调用auto_ptr_ref版本的赋值操作符
}

auto_ptr<int>(new int(0卡塔尔State of Qatar是一个最近对象,同期是三个右值,平时的拷贝布局函数当然能拷贝右值,因为其参数类别必需为贰个const reference, 不过我们知晓,auto_ptr的拷贝函数其参数类型为reference,所以,为了使那行代码能经过,大家引进auto_ptr_ref来兑现从右值向左值的调换。其进度为:

auto_ptr_ref之本质

1卡塔尔(قطر‎ ap1要通过拷贝 auto_ptr<int>(new int(0卡塔尔国卡塔尔来布局本人--------------------->明显必要拷贝函数布局

本质上,auto_ptr_ref赋予了auto_ptr“援引”的语义,那或多或少方可从auto_ptr_ref的讲解看出:

2) auto_ptr<int>(new int(0卡塔尔国卡塔尔作为右值与现存的五个拷贝布局函数参数类型都无可奈何协作,也无从调换来该种参数类型----------------->现成拷贝构造函数无法知足

/**
   *  A wrapper class to provide auto_ptr with reference semantics.
   *  For example, an auto_ptr can be assigned (or constructed from)
   *  the result of a function which returns an auto_ptr by value.
   *
   *  All the auto_ptr_ref stuff should happen behind the scenes.
   */
  template<typename _Tp1>
    struct auto_ptr_ref
    {
      _Tp1* _M_ptr;
      
      explicit
      auto_ptr_ref(_Tp1* __p): _M_ptr(__p) { }
    };

3卡塔尔国发掘救助的拷贝构造函数auto_ptr(auto_ptr_ref<T> rhsState of Qatar throw(卡塔尔------------->支持的正片结构函数能够知足

auto_ptr_ref之代码

4) 试图将auto_ptr<int>(new int(0))转换成auto_ptr_ref<T>----------->

此间列出auto_ptr_ref相关的函数,共仿照效法:

5) 发现类型转换函数operator auto_ptr_ref<Y>(卡塔尔 throw(卡塔尔国,转换来功

auto_ptr(auto_ptr_ref<element_type> __ref) throw()
: _M_ptr(__ref._M_ptr) {}

6)调用auto_ptr(auto_ptr_ref<T>rhs)throw()完成auto_ptr_ref向auto_ptr的构造。

auto_ptr&
operator=(auto_ptr_ref<element_type> __ref) throw () {
    if (__ref._M_ptr != this->get()) {
        delete _M_ptr;
        _M_ptr = __ref._M_ptr;
    }
    return *this;
}

据此通过一个直接类成功的落到实处了拷贝构造右值(不常对象)

template<typename _Tp1>
operator auto_ptr_ref<_Tp1>() throw () {
    return auto_ptr_ref<_Tp1> (this->release());
}

再正是,这一个扶植方法不会使const auto_ptr被拷贝, 原因是在第5步, 此类型转变函数为non-const的,大家精晓,const对象是力不能及调用non-const成员的, 所以调换失利。当然, 这里有一个主题素材要细心, 假诺你把这一个帮衬调换的代码注释掉,该行代码依然恐怕得逞编写翻译,这是为啥吧?debug一下, 大家能够发掘只调用了二遍架构函数,而拷贝布局函数并从未被调用,原因在于编译器将代码优化掉了。那种类型优化叫做returned value optimization,它能够使得防护部分抽象的近年来对象的布局。当然,前提是你的编译器要支持returned value optimization。

template<typename _Tp1>
operator auto_ptr<_Tp1>() throw () {
    return auto_ptr<_Tp1> (this->release());
}

参照他事他说加以考查资料:

auto_ptr之变迁

auto_ptr完毕之我见

auto_ptr_ref的奇妙(上)

auto_ptr_ref的奇妙(下)

auto_ptr_ref 的目标是哪些

关于auto_ptr_ref的少数主题材料

左值和右值

注:

关于左值和右值

L = Location 可寻址
R = Read 可读

本文由10bet手机官网发布于web前端,转载请注明出处:为什么需要auto_ptr_ref

上一篇:之运算符重载和虚表指针 下一篇:eclipse常用快捷键10bet手机官网:
猜你喜欢
热门排行
精彩图文