• pointer or function


    首先说明一下仿指针与仿函数是什么,指针我们知道是一个地址,我们可以利用指针来访问它所指向的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。

  • 相关阅读:
    Charles关于Https SSLHandshake解决备忘录
    图片Image转换为base64编码的方法
    图解HTTP(三)
    图解HTTP(二)
    图解HTTP(一)
    Linux下which、whereis、locate、find命令作用
    jQuery实现图片上传
    常见前端面试题备注
    css实现正方形div的3种方式
    promise
  • 原文地址:https://www.cnblogs.com/xiangqi/p/14289952.html
Copyright © 2020-2023  润新知