body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}

资源管理--RAII:
1、RAII(Resource Acquisition Is Initialization)是一种由 C++创造者 Bjarne Stroustrup 提出的, 利用对象生命周期管理程序资源(包括内存、文件句柄、锁等)的技术。
2、使用 RAII 时,一般在资源获得的同时构造对象, 在对象生存期间,资源一直保持有效;对象析构时,资源被释放。
关键:要保证资源的释放顺序与获取顺序严格相反
3、RAII类的常见特征
1、在构造时初始化资源, 或托管已构造的资源
2、析构时释放资源
3、一般不允许复制或赋值(对象语义)
4、提供若干访问资源的方法
4、RAII的本质是用栈对象来管理资源,因为栈对象在离开作用域时,会自动调用析构函数
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
class SafeFile
{
        public:
                SafeFile(const string & filename)
                :_fp(fopen(filename.c_str(),"w+"))
                {
                        cout<<"fopen(_fp)"<<endl;
                        if(NULL == _fp)
                        {
                                cout<< "FILE open error"<<endl;
                        }
                }
                ~SafeFile()
                {
                        if(_fp != NULL)
                        {
                                fclose(_fp);
                                cout<< "fclose(_fp) "<<endl;
                        }
                }

                void write(const char * str)
                {
                        cout<<"write(const char *)"<<endl;
                        if(fputs(str,_fp)==EOF)
                        {
                                cout<< "write error!" <<endl;
                        }
                }
        private:
                FILE * _fp;
                SafeFile(const SafeFile & rhs);
                SafeFile & operator = (const SafeFile & rhs);
};

int main()
{
        SafeFile sf("test.txt");
        sf.write("hello,world!\n");
        return 0;
}

/**
**利用一个对象在离开个域中会调用析构函数的特性,
**在构造函数中完成初始化,在析构函数中完成清理工作,将需要
**操作和保护的指针作为成员变量放入RAII中。
*/

#include<iostream>
using namespace std;
template<typename T>
class RALL
{
        public:
                RALL(T* p):_p(p)  { cout<<"托管资源"<<endl; }
                ~RALL()  // 托管资源,可能多个指针指向同一块内存区域,if判断不能发挥作用
                {
                        if(NULL!=_p)
                        {
                                delete _p;
                                _p=NULL;
                        } cout<<"释放资源"<<endl;
                }
                // 资源指针,被托管的都是资源的地址,
                // 所以要重载这些运算符,能够获取
                // 资源的地址
                T* get()const;
            T& operator*()const;  //目的就是返回托管的指针指向的值
            T* operator->()const;  // 似乎没怎么用到这个
                void reset(T* new_p);  // 更改托管资源地址
                void swap(RALL<T> &rhs);  // 交换地址
        private:
                RALL(const RALL<T>&);  //禁止调用拷贝构造函数
                RALL<T>& operator=(const RALL<T>&);  // 禁止调用赋值构造函数
        private:
                T* _p;  // _p指向被托管的资源
};
template<typename T>
T* RALL<T>::get()const
{
        return _p;
}
template<typename T>
T& RALL<T>::operator*()const
{
        return *_p;
}
template<typename T>
T* RALL<T>::operator->()const
{
        return _p;
}
        template<typename T>
void RALL<T>::reset(T* new_p)
{
        delete _p;
        _p = new_p;
}
        template<typename T>
void RALL<T>::swap(RALL<T> &rhs)
{
        std::swap(_p,rhs._p);
}

#include<iostream>
#include"RALL.h"
using namespace std;
class resource
{
        public:
                resource(){cout<<"resource::resouce()"<<endl;}
                ~resource(){cout<<"resource::~resource()"<<endl;}
        private:
};
class test
{
        public:
                test():_p(new resource){ cout<<"test::test()"<<endl; }
             ~test(){ cout<<"test::~test()"<<endl; }
                //拷贝构造函数
                //RALL类重载了*运算符,这里new的同时
                //直接用rhs的值来初始化新开辟的空间
                test(const test& rhs):_p(new resource(*rhs._p))
                { cout<<"test::test(const test&)"<<endl; }
//                test& operator=(const test& rhs)
//                {
//                        cout<<"test::operator=(const test&)"<<endl;
//                        if(this==&rhs){return *this;}
//                        //delete this->_p.get();
//                        //*_p = *(rhs._p);
//                        _p.reset((rhs._p).get());  // 函数里面已经写了释放
//                        return *this;
//                }
        private:
                test& operator=(const test& rhs);  // 赋值构造函数造成多个指针指向一块区域
                // dubble free
        private:
                RALL<resource> _p;
};
int main()
{
        test em;
        cout<<"--------------------------"<<endl;
        test em2;
        cout<<"**************************"<<endl;
        //em2 = em;  // 这里赋值了会导致em2和em释放,段错误
        //后面我在RALL类里面的析构函数做了特殊处理,指针为NULL才delete
//结果没考虑多个指针指向一块区域
        cout<<"准备开始释放资源:"<<endl;
        return 0;
}


资源管理--智能指针:
      ——是存储指向动态分配(堆)对象指针的类
      ——在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象
      ——RAII类模拟智能指针
C++11提供了以下几种智能指针,位于头文件  #include<memory> ,它们都是类模板
std::auto_ptr(复制/赋值)   现在已淘汰
std::unique_ptr  c++11
std::shared_ptr  c++11
std::weak_ptr    c++11
g++ -std=c++11 xx.cc
1、std::auto_ptr在构造时获取对某个对象的所有权(ownership),在析构时释放该对象
2、std::auto_ptr要求其对“裸”指针的完全占有性---->
      ——在拷贝构造或赋值操作时,会发生所有权的转移      //两次使用auto_ptr,第一次的会转移,转移了之后就失效了,其后不能使用,只能最终获得转移权的智能指针使用
3、本身存在缺陷
#include <iostream>
#include <memory>
using namespace std;
int main()
{
        double * pd = new double(88.99);
        auto_ptr<double> app(pd);
        cout<<"*app = "<<*app<<endl;
        cout<<"app.get()= "<<app.get()<<endl;
        cout<<"pd       = "<<pd<<endl;
        int * pi = new int(5);
        auto_ptr<int> api(pi);
        auto_ptr<int> api2(api);   //这里实际上是拷贝
        //表达的是值语义,但是实现有缺陷,在底层已经发生了所有权的转移
        cout<<"*api2 = "<<*api2<<endl;
        cout<<"*api = "<<*api<<endl;  //段错误,api已经不能在使用了
        return 0;
}


4、std::unique_ptr是一个独享所有权的智能指针,它提供了一种严格语义上的所有权,包括:
     ——拥有它所指向的对象
     ——无法进行复制、赋值操作
     ——保存指向某个对象的指针,当它本身被删除释放的时候,会使用给定的删除器释放它指向的对象
     ——具有移动(std::move)语义(和前面的把左值改右值一样),可做为容器元素
1.无法进行复制、赋值操作
  std::unique_ptr<int> ap(new int(88 );
  std::unique_ptr<int> one (ap) ; // 会出错
  std::unique_ptr<int> two = one; //会出错

3.可做为容器元素
  unique_ptr<int> sp(new int(88));
  vector<unique_ptr<int> > vec;
  vec.push_back(std::move(sp));
  //vec.push_back( sp ); 这样不行,会报错的.
  //cout<<*sp<<endl;但这个也同样出错,说明sp添加到容器中之后,它自身报废了.

2.可以进行移动构造和移动赋值操作
 unique_ptr<int> GetVal( ){
    unique_ptr<int> up(new int(88 );
    return up;
 }
 unique_ptr<int> uPtr = GetVal();   //ok
 实际上上面的的操作有点类似于如下操作
 unique_ptr<int> up(new int(88 );
 unique_ptr<int> uPtr2 = std::move(up) ; //这里是显式的所有权转移. 把up所指的内存转给uPtr2了,而up不再拥有该内存.
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
unique_ptr<int> getValue()
{
        unique_ptr<int> upi(new int (88));
        return upi;  //最后获得的是一个右值
}
int main()
{
//unique_ptr无法进行复制或赋值,表达的是对象语义
        unique_ptr<int> one(new int(1));
        //无法进行复制、赋值操作
//        unique_ptr<int> two(one);   //错
//        unique_ptr<int> three=one; //错
 
//可以进行移动构造和移动赋值操作
// 调用的是移动构造函数
        unique_ptr<int> tmp = getValue();
        unique_ptr<int> up(new int(88));
        unique_ptr<int> up2 = move(up); 
//这里把显示的左值所有权转移,把up所指的内存转移给up2,而up不再拥有该内存
//    cout<<" *up = "<<*up<<endl;  //段错误,move之后不能再使用
        //可做为容器元素
       unique_ptr<int> sp(new int(33));
       vector<unique_ptr<int> > vec;
        vec.push_back(move(sp));     //必须转换成右值
//     cout<<*sp<<endl;      //段错误,在添加到容器的过程中转换成右值,自身不再拥有内存
        return 0;
}


5、std::shared_ptr是一个引用计数智能指针,用于共享对象的所有权
     ——引进了一个计数器shared_count,用来表示当前有多少个智能指针对象共享指针指向的内存块
     ——析构函数中不是直接释放指针对应的内存块,如果shared_count大于0则不释放内存只是将引用计数减1,只有计数等于0时释放内存
     ——复制构造与赋值操作符只是提供一般意义上的复制功能,并且将引用计数加1.
     ——问题:循环引用(最终导致内存泄露)
#include<iostream>
#include<memory>
using namespace std;
int main()
{
        shared_ptr<int> sp(new int(1));
        cout<<"*sp="<<*sp<<"  sp.use_count="<<sp.use_count()<<endl;
        shared_ptr<int> sp2 = sp;  // 赋值,只是引用计数增加1
        cout<<"*sp="<<*sp<<"  sp.use_count="<<sp.use_count()<<endl;
        cout<<"*sp2="<<*sp2<<"  sp2.use_count="<<sp2.use_count()<<endl;
}

6、std::shared_ptr是强引用智能指针
强引用,只要有一个引用存在,对象就不能被释放

#include <iostream>
#include <memory>
using namespace std;
class Parent;    //前向声明
class Child
{
        public:
                Child()   {  cout<< "Child()" <<endl;  }
                ~Child()  {  cout<< "~Child()" <<endl; }
                shared_ptr<Parent> _parentPtr;
};
class Parent
{
        public:
                Parent()  {  cout<< "Parent()" <<endl;  }
                ~Parent() {  cout<< "~Parent()" <<endl; }
                shared_ptr<Child> _childPtr;
};
int main()
{//问题是:循环引用,发生内存泄露
        shared_ptr<Parent> parentPtr(new Parent);
        shared_ptr<Child> childPtr(new Child);
        cout<< "parent' use_count()= "<<parentPtr.use_count()<<endl;
        cout<< "child' use_count()= "<<childPtr.use_count()<<endl;
        parentPtr->_childPtr = childPtr;
        childPtr->_parentPtr = parentPtr;
        cout<< "parent' use_count()= "<<parentPtr.use_count()<<endl;
        cout<< "child' use_count()= "<<childPtr.use_count()<<endl;
        return 0;
}


7、std::weak_ptr 是弱引用智能指针
     ——强引用,只要有一个引用存在,对象就不能被释放
     ——弱引用,并不增加对象的引用计数,但它知道对象是否存在。如果存在,提升为shared_ptr成功;否则,提升失败
     ——通过weak_ptr访问对象的成员的时候,要提升为shared_ptr
shared_ptr的误用
class std::enable_shared_from_this
方法shared_from_this()
删除器
#include <iostream>
#include <memory>
using namespace std;
class Parent;    //前向声明
class Child
{
        public:
                Child()   {  cout<< "Child()" <<endl;  }
                ~Child()  {  cout<< "~Child()" <<endl; }
                weak_ptr<Parent> _parentPtr;    //弱引用  
};
class Parent
{
        public:
                Parent()  {  cout<< "Parent()" <<endl;  }
                ~Parent() {  cout<< "~Parent()" <<endl; }
                shared_ptr<Child> _childPtr;
                //weak_ptr<Child> _parentPtr;  
};
int main()
{//问题是:循环引用,发生内存泄露;使用weak_ptr能够打破循环引用
        shared_ptr<Parent> parentPtr(new Parent);
        shared_ptr<Child> childPtr(new Child);
        cout<< "parent' use_count()= "<<parentPtr.use_count()<<endl;
        cout<< "child' use_count()= "<<childPtr.use_count()<<endl;
        parentPtr->_childPtr = childPtr;
        childPtr->_parentPtr = parentPtr;
        cout<< "parent' use_count()= "<<parentPtr.use_count()<<endl;
        cout<< "child' use_count()= "<<childPtr.use_count()<<endl;
        return 0;
}

#include<iostream>
#include<memory>
using namespace std;
class X
{
        public:
                X(){ cout<<"X()"<<endl; }
                ~X(){ cout<<"~X()"<<endl; }
                void fun(){ cout<<"X::fun()"<<endl; }
};

int main()
{
        weak_ptr<X> wp;
        {
                cout<<"wp use count="<<wp.use_count()<<endl;  // weak_ptr没有get成员
                cout<<endl;
                shared_ptr<X> sp(new X);
                cout<<"addr ap="<<sp.get()<<"  sp use count="<<sp.use_count()<<endl;
                cout<<endl;
                wp = sp;  // 并没有增加引用计数
                cout<<"addr ap="<<sp.get()<<"  sp use count="<<sp.use_count()<<endl;
                cout<<"wp use count="<<wp.use_count()<<endl;
                cout<<endl;
                shared_ptr<X> sp2 = wp.lock();  // 弱引用要提升为强引用才能访问托管对象
                cout<<"addr ap="<<sp.get()<<"  sp use count="<<sp.use_count()<<endl;
                cout<<"wp use count="<<wp.use_count()<<endl;
                cout<<"addr sp2="<<sp2.get()<<"  sp2 use count="<<sp2.use_count()<<endl;
                if(!sp2){  cout<<"object is destroyed!"<<endl;  }
                else
                { // 引用计数+1
                        sp2->fun();
                        cout<<"weak_ptr lock 成功"<<endl;
                }
        }  // 离开作用域,sp、sp2被释放  weak_ptr知道自己托管的对象是否释放了

        cout<<endl;
        shared_ptr<X> sp3 = wp.lock();  // new X的对象已经释放了,提升失败
        if(!sp3){  cout<<"object is destroyed!"<<endl;  }
        else
        { 
                sp3->fun();
                cout<<"weak_ptr lock 成功"<<endl;
        }
        cout<<"wp use count="<<wp.use_count()<<endl;
        cout<<"addr sp3="<<sp3.get()<<"  sp3 use count="<<sp3.use_count()<<endl;
        return 0;
}

8、shared_ptr的误用
     ——class std::enable_shared_from_this
     ——方法  shared_from_this()
     ——删除器
class A:public enable_share_from_this<A>

使用场合:当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr。
我们就使类A继承enable_share_from_this,然后通过其成员函数share_from_this()返回当指向自身的share_ptr。
以上有2个疑惑:
1.把当前类对象作为参数传给其他函数时,为什么要传递share_ptr呢?直接传递this指针不可以吗?
    一个裸指针传递给调用者,谁也不知道调用者会干什么?假如调用者delete了该对象,而share_ptr此时还指向该对象。
2.这样传递share_ptr可以吗?share_ptr<this>
    这样会造成2个非共享的share_ptr指向一个对象(拷贝)(一个指针被两个对象托管),最后造成2次析构该对象。
#include<iostream>
#include<memory>
using namespace std;
class point:public enable_shared_from_this<point>
{
        public:
                point(int x=0,int y=0):_x(x),_y(y)
                { cout<<"point(int,int)"<<endl; }
                ~point()  { cout<<"~point()"<<endl; }
#if 0
                point* add(const point* rhs)
                {
                        _x += rhs->_x;
                        _y += rhs->_y;
                        return this;
                }
#endif
                shared_ptr<point> add(const point* rhs)
                {
                        //在类内部进行托管
                        _x += rhs->_x;
                        _y += rhs->_y;
                        //return shared_ptr<point>(this); 
// 和point* add(cosnt point*)一个效果。
                        return shared_from_this();
                        //这个方法在enable_shared_from_this<class T>中
                }
                friend ostream& operator<<(ostream& ,const point&);
        private:
                int _x;
                int _y;
};
ostream& operator<<(ostream& os,const point& rhs)
{
        os<<"("<<rhs._x<<","<<rhs._y<<")";
}

void test1()//对shared_ptr误用
{ //1、对shared_ptr的误用
        point* p1 = new point(1,2);
        shared_ptr<point> sp1(p1);
        cout<<"sp1 use count="<<sp1.use_count()<<endl;
        shared_ptr<point> sp2(p1);  //只有sp2 = sp1才会提升引用计数
//      shared_ptr<point> sp2(sp1);  //这样也会提升引用计数
        //重复对一个对象托管,会导致多次调用析构函数
        cout<<"sp1 use count="<<sp1.use_count()<<endl;
        cout<<"sp2 use count="<<sp2.use_count()<<endl;
}
void test2()
{
        shared_ptr<point> p1(new point(1,2));
        shared_ptr<point> p2(new point(3,4));
        cout<<"p1 use count="<<p1.use_count()<<"  p2 use count="<<p2.use_count()<<endl;
        p2.reset(p1.get());  // reset先释放p2,在把p1的值赋给p2
        cout<<"addr p1="<<p1<<endl;
        cout<<"addr p2="<<p2<<endl;
        cout<<"p1 use count="<<p1.use_count()<<"  p2 use count="<<p2.use_count()<<endl;
}
void test3()
{   
        shared_ptr<point> p1(new point(1,2));     
        shared_ptr<point> p2(new point(3,4));    
        //误用, p1和p3同时托管一个对象   
        shared_ptr<point> p3(p1->add(p2.get()));   
        cout<<"p1 use count="<<p1.use_count()<<endl;   
        cout<<"p2 use count="<<p2.use_count()<<endl;   
        cout<<"p3 use count="<<p3.use_count()<<endl;         
        cout<<"p1="<<p1<<"  p2="<<p2<<"  p3="<<p3<<endl;   
}
int main()
{
        //test1();  // 这里test1()和test2()同时打开段错误,不知道为啥
        cout<<"---------------"<<endl;
        //test2();  // 和上面同样的原因,我猜测是前面开辟的堆内存没有释放,后面又要来delete,所以出现错误。
        cout<<"---------------"<<endl;
        test3();
        return 0;
}

//point(int,int)
//sp1 use count=1
//sp1 use count=1
//sp2 use count=1
//~point()
//~point()
//---------------
//point(int,int)
//point(int,int)
//p1 use count=1  p2 use count=1
//~point()
//addr p1=0x244e030
//addr p2=0x244e030
//p1 use count=1  p2 use count=1
//~point()
//~point()
//---------------
//point(int,int)
//point(int,int)
//p1 use count=1
//p2 use count=1
//p3 use count=1
//p1=0xbd5030  p2=0xbd5070  p3=0xbd5030
//~point()
//~point()
//~point()
//采用返回shared_from_this()
//---------------
//point(int,int)
//point(int,int)
//p1 use count=2
//p2 use count=1
//p3 use count=2
//p1=0xc2f030  p2=0xc2f070  p3=0xc2f030
//~point()
//~point()

#include <iostream>
#include <stdio.h>
#include <memory>
using namespace std;
struct Fpcloser
{
        void operator()(FILE * fp)
        {
                if(fp)
                {
                        cout<<"release file pointer!"<<endl;
                        fclose(fp);
                }
        }
};
void test0()
{//自己指定删除器
        unique_ptr<FILE,Fpcloser> up( fopen("test.txt","w+"),Fpcloser() );
        fputs("hello,world\n",up.get());     //get()返回托管对象的指针
}
void test1()
{//指定删除器的方式
        shared_ptr<FILE> sp(fopen("test.txt","r+"),Fpcloser());
        char buff[1024];
        fgets(buff,sizeof(buff),sp.get());
        cout<<buff;
}
int main()
{
        //test0();
        test1();
        return 0;
}

资源管理
》原理:利用栈对象去管理资源;栈对象的特性是创建对象是自动调用构造函数,当其生命周期结束时,会自动调用析构函数
》RALL
》智能指针
   auto_ptr  (已被废弃)
   unique_ptr
   shared_ptr
   weak_ptr

RALL资源获取初始化,删除器的更多相关文章

  1. C++进阶--RAII 资源获取即初始化

    //############################################################################ /* 资源获取即是初始化 (RAII) * ...

  2. ServletContext类 (共享数据+获取初始化的参数+请求转发+读取资源文件)

    ServletContext对象 web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的 web应用: 作用 1.共享数据  (一般用sessio ...

  3. WebGL 创建和初始化着色器过程

    1.编译GLSL ES代码,创建和初始化着色器供WebGL使用.这些过程一般分为7个步骤: 创建着色器对象(gl.createBuffer()); 向着色器对象中填充着色器程序的源代码(gl.shad ...

  4. Spark源码剖析 - SparkContext的初始化(八)_初始化管理器BlockManager

    8.初始化管理器BlockManager 无论是Spark的初始化阶段还是任务提交.执行阶段,始终离不开存储体系.Spark为了避免Hadoop读写磁盘的I/O操作成为性能瓶颈,优先将配置信息.计算结 ...

  5. C++几个技巧:智能指针在消息传递中的使用,元组,及lambda删除器

    1.SendMessage/PostMessage中传递对象参数 (1)方法1:使用shared_ptr 发送端: PostMessage(MyhWnd, CWM_SOME_ERROR, 0, rei ...

  6. iOS UIWebView 和 WKWebView 的 cookie 获取,设置,删除

    Cookie简介说到Cookie,或许有些小伙伴会比较陌生,有些小伙伴会比较熟悉.如果项目中,所有页面都是纯原生来实现的话,一般Cookie这个东西或许我们永远也不会接触到.但是,这里还是要说一下Co ...

  7. Java并发包源码学习系列:CLH同步队列及同步资源获取与释放

    目录 本篇学习目标 CLH队列的结构 资源获取 入队Node addWaiter(Node mode) 不断尝试Node enq(final Node node) boolean acquireQue ...

  8. cookies的获取,删除,设置

    cookies,sessionStorage 和 localStorage 的区别? 1.cookie在浏览器和服务器间来回传递. sessionStorage和localStorage不会: 2.s ...

  9. SpringMVC核心——参数获取与Servlet资源获取问题

    一.SpringMVC 使用 @PathVariable.@RequestParam.@RequestHeader.@CookieValue 等来解决参数获取问题. 1. @PathVariable: ...

随机推荐

  1. python之动态参数 *args,**kwargs(聚合,打散--转载

    转自https://www.cnblogs.com/ellisonzhang/p/10243122.html 一.函数的动态参数 *args,**kwargs, 形参的顺序 1.你的函数,为了拓展,对 ...

  2. 为什么Log.nsf中存储的日志只有最近7天的原因

    是由于Domino服务器的notes.ini配置文件中有一行参数: Log = logfilename, log_option, not_used, days, size 比如:Log=log.nsf ...

  3. (转)在 ListViewItem 上拖动进行框选

    public partial class Form1 : Form { private bool IsMouseDown = false; Rectangle MouseRect = Rectangl ...

  4. EditPlus 4.3.2475 中文版已经发布(10月28日更新)

    新的修订版修复了上移多个插入点时会造成程序崩溃的问题.

  5. react 修改循环列表的当前单个子项状态:思路 拿原始state数据更改,再做请求

    handleLike(item,index){ var id = item.id; _ENV.post( _ENV.HOST+'/communion/video-up', {'user_id':loc ...

  6. table--边框样式设置

    Table的一些设置(自适应以及溢出)   table的两个属性 单行溢出点点显示 表格的宽度设置 双栏自适应连续连续英文符换行 1.table重置的两个属性: ①border-collapse: c ...

  7. Fiddler 手机无法上网问题

    一.Fiddler版本升级后需要升级netframework 二.每一个域名点击需要授权 出现此页面依次点击显示详细信息和访问此网站. 有时候网站白板,可能是cdn域名没有授权的原因,可以直接在url ...

  8. SQL学习笔记之项目中常用的19条MySQL优化

    在写文章之前,首先感谢 飞友科技 陆老师提供的文档.. 声明一下:下面的优化方案都是基于 “ Mysql-索引-BTree类型 ” 的 0x00 EXPLAIN 做MySQL优化,我们要善用 EXPL ...

  9. 升级 Jenkins 从 1.56 到 2.64

    今天上午小伙伴突然发现 git 的 Credential 出错了,同时页面的保存按钮也找不到.折腾了半天没有头绪,干脆升级到最新的版本吧,毕竟也很久没升级了. 从 1.x 到 2.x 有很多改动,为了 ...

  10. Vim 操作命令不完全汇总

    .命令:"重复上次修改": x命令:删除光标下的字符: u命令:撤销上次修改: dd命令:删除整行: >G命令:增加从当前行到文档末尾处的层级缩进: $命令:把光标移至行尾: ...