Loading

pointer or function - like classes[仿指针与仿函数]

首先说明一下仿指针与仿函数是什么,指针我们知道是一个地址,我们可以利用指针来访问它所指向的value,仿指针就是用一个类去实现指针的作用,那么我们为什么要特地写一个类去实现指针呢,因为在我们开发过程中,我们希望指针可以有更多功能,而不仅仅是指向一个地址,那么我们就可以通过一个类去实现指针的功能的前提下,给它加上更多的功能,来满足我们的需求,比如说我们每使用一次指针,都要去释放掉这个指针,如果忘记释放,就会造成内存泄露,但我们无法使指针自己释放,但是如果是指针类,我们就可以在析构函数中加上delete语句,那么以后使用这个指针,就再也不用管它的释放问题了,C++特性中的智能指针其实就是这种原理,仿函数也是类似道理。

 

_ pointer-like classes

简单用代码实现一个智能指针,其实就是老版本的shared_ptr指针,可以明白其中的运作原理

#include<iostream>
using namespace std;

template<class T>
class shared_ptrc 
{
public:
    T& operator*() const
    {
        return *px;
    } 
    T* operator->() const
    {
        return px;
    }

    shared_ptrc(T* p) : px(p){}
    ~shared_ptrc() { delete px; }
private:
    T*      px;
    long* pn;
};

struct Foo
{
    void method() { cout << "Foo::method" << endl; }
};

int main() {
    shared_ptrc<Foo> sp(new Foo);
    Foo f(*sp);

    sp->method();
    system("pause");
    return 0;
}

 

首先从它的重载说起, 可以看到

    T& operator*() const
    {
        return *px;
    } 

 

重载了*号,并且无参数,这样就符合了指针的语法,我们平常建立一个指针比如int * p;  *p代表就是它所指的value,再看这里,返回 *px,而px又是T类的指针(T * px),所以这里就是返回一个value,并且函数开头是T&,

之前说了返回value的reference比直接返回value普遍要快很多,所以这里返回了一个reference。

我们再看

 T* operator->() const
    {
        return px;
    }

当我们使用"->"时,是不是一般都是调用指针所指内容的方法或者属性,所以我们必须要返回指针类型,所以这里是 T* operator->() ,(如果这里不明白可以看之前说的引用与指针的区别)

所以现在来看,我们是不是就可以把shared_ptrc当作一个指针类型来使用了

 shared_ptrc<Foo> sp(new Foo);

这样就是导入Foo模板,并且new一个Foo类对象,让shared_ptrc类中的px指针指向它,然后当我们使用完这个指针后,程序结束后,它会调用析构函数自动销毁,无需手动释放,这就是仿指针的基本用法。

迭代器其实也是一种仿指针,平常使用迭代器我们都有接触过,比如创建一个vector数组的迭代器,vector<int>::iterator it;我们经常会使用这种语法 it++,++it,使迭代器指针指向数组中的下一个内容,这其实就是在指针类中重载了++符号来实现的。

 

为了更加了解仿指针,我们可以看一看C++的智能指针,这样也能使我们对使用指针的风险更加了解。

有4种智能指针:auto_ptr(C++11已弃用), unique_ptr,shared_ptr, weak_ptr 

首先看看智能指针为我们解决的问题,其实跟上面所说的基本一样。

当我们使用普通指针时:

void func(string & str)
{
    ...
    string * s = new string(str);
    if (error())
        throw exception();
    str = *s; 
    delete s;
    ...
    return;
}

 

当程序出错,抛出异常,可以很明显看到发生了一个严重的问题,s指针未被释放,程序就中止了,导致内存泄露。

而我们如果使用智能指针auto_ptr

void func(string & str)
{
    ...
    auto_ptr<string> s(new string(str));
    if (error())
        throw exception();
    str = *s; 
    ...
    return;
}

 

程序抛出异常,auto_ptr会调用内部析构函数,释放掉指针,这样就避免了内存泄漏。

然后说一下为什么auto_ptr会被抛弃,auto_ptr的工作模式是拥有所有权。对于特定的对象,只能有一个智能指针可拥有,这样只有拥有对象的智能指针的析构函数会删除该对象。赋值操作会转让所有权。

可以看下面这一串代码:

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

int main() {
  auto_ptr<string> test[3] =
 {
  auto_ptr<string> (new string("helloworld1")),
  auto_ptr<string> (new string("helloworld2")),
  auto_ptr<string> (new string("helloworld3")),
 };
 auto_ptr<string> p;
 p = test[2]; // test[2]所有权被p拿走,此时test[2]为一个空指针。

 cout<<test[2]<<endl; //运行时报错

 return 0;
}

可以看出auto_ptr存在内存泄漏的潜在风险,所以不再使用auto_ptr,unique_ptr也是所有权模式,但是如果将上面auto_ptr换成unique_ptr的话,不会在运行程序时报错,而是会在编译时报错,这样也可以避免内存泄漏的潜在风险。

 

shared_ptr与weak_ptr则是另外一种模式,这里就不详细说明了,以后专门再用一篇博客描述4种智能指针。

 

_ function-like classes

仿函数也是用一个模板类去实现的,实现原理与仿指针差不多,由于目前不经常使用,了解不是很深,就先把代码语法问题解决了,以后再补充说明仿函数的使用环境与时机。

template <class T>
struct identity{
    const T&
    operator() (const T& x) const {return x;}
};

template <class Pair>
struct select1st   {
    const typename Pair::first_type&
    operator() (const Pair& x) const
    { return x.first;}
};

template <class Pair>
struct select2nd  {
    const typename Pair::second_type&
    operator() (const Pair& x) const
    { return x.second;}
};

template <class T1,class T2>
struct pair{
    T1 first;
    T2 second;
    pair() : first(T1()),second(T2()){}
    pair(const T1& a, const T2& b)
        : first(a),second(b) {}
...
};

 

可以看到select1st与select2nd可以当作函数来使用,分别返回pair的两个value。

 

posted @ 2021-01-17 19:58  eveilcoo  阅读(175)  评论(0编辑  收藏  举报