使用ProtoBuffer 自带压缩功能

使用ProtoBuffer 自带压缩功能

起因和本文想要说明的内容

消息中心数据存储时候,是使用key-value的形式来存储邮件,但是protobuf在默认的序列化/反序列化的时候是不进行压缩的。
如果想使用 ProtoBuffer的压缩功能,还需要多做几步,构造一个InputStream/OutputStream传入传出内容。ProtoBufffer可选的 zlib压缩 gzip 压缩,压缩效率极高。对于当前项目来说,压缩之后的数据大小为原来大小的: 15~40%(消息体越大,压缩率越大)(目前正常的邮件信息都超过1k),实用性很强。
网上关于这个部分内容较少,有不少关于这方面的提问,但是回答都语焉不详,官方并没有提供这方面的例子,对于初学者来说想要用好并不容易。
另外,protobuf的这一个InputStream/OutputStream的设计也有非常好的参考价值,大家在写自己的IO函数实现的时候也可以参考这个设计。
因此,写一个走读笔记,供参考。

目标:

  1. 如何使用Protobuffer的压缩功能;
  2. ProtoBuffer io 模块设计方法理解。

Protobuf序列化函数列表

要使用ProtoBuf的压缩功能,则主要用到的bool SerializeToZeroCopyStream(io::ZeroCopyOutputStream* output) const; 函数。
需要实现一个派生之后的ZeroCopyOutputStream函数体。

// Write a protocol buffer of this message to the given output.  Returns
// false on a write error.  If the message is missing required fields,
// this may GOOGLE_CHECK-fail.
bool SerializeToCodedStream(io::CodedOutputStream* output) const;
// Like SerializeToCodedStream(), but allows missing required fields.
bool SerializePartialToCodedStream(io::CodedOutputStream* output) const;
// Write the message to the given zero-copy output stream.  All required
// fields must be set.
bool SerializeToZeroCopyStream(io::ZeroCopyOutputStream* output) const;
// Like SerializeToZeroCopyStream(), but allows missing required fields.
bool SerializePartialToZeroCopyStream(io::ZeroCopyOutputStream* output) const;
// Serialize the message and store it in the given string.  All required
// fields must be set.
bool SerializeToString(string* output) const;
// Like SerializeToString(), but allows missing required fields.
bool SerializePartialToString(string* output) const;
// Serialize the message and store it in the given byte array.  All required
// fields must be set.
bool SerializeToArray(void* data, int size) const;
// Like SerializeToArray(), but allows missing required fields.
bool SerializePartialToArray(void* data, int size) const;

// Make a string encoding the message. Is equivalent to calling
// SerializeToString() on a string and using that.  Returns the empty
// string if SerializeToString() would have returned an error.
// Note: If you intend to generate many such strings, you may
// reduce heap fragmentation by instead re-using the same string
// object with calls to SerializeToString().
string SerializeAsString() const;
// Like SerializeAsString(), but allows missing required fields.
string SerializePartialAsString() const;

// Like SerializeToString(), but appends to the data to the string's existing
// contents.  All required fields must be set.
bool AppendToString(string* output) const;
// Like AppendToString(), but allows missing required fields.
bool AppendPartialToString(string* output) const;

如何使用ProtoBuffer压缩功能

  1. 构造一个ZeroCopyOutputStream的派生函数,负责提供压缩解压的内存空间;
  2. 正确调用GzipInputStream,获取结果。

定义ZeroCopyOutputStream派生函数

class CPBufferInputStream: public google::protobuf::io::ZeroCopyInputStream
{
    public:
        CPBufferInputStream() {}
        virtual ~CPBufferInputStream();
        virtual bool Next(const void** data, int* size)
        {
            *data=m_szBuffer+m_idx;
            *size=m_size-m_idx;
            m_idx+=m_size;
            return true;
        }
        virtual void BackUp(int count)
        {
            m_idx-=count;
        }
        virtual bool Skip(int count)
        {
            if(m_idx+count==m_size)
            {
                m_idx+=count;
                return false;
            }
            else if(m_idx+count>m_size)
            {
                return false;
            }

            m_idx+=count;
            return true;

        }
        virtual int64 ByteCount()
        {
            return m_idx;
        }

    private:
        ::google::protobuf::int64 m_idx;
        ::google::protobuf::int64 m_size;
        char m_szBuffer[PROTOBUFFER_MAX_BUFFER_SIZE];
};

class CPBufferOutputStream: public google::protobuf::io::ZeroCopyOutputStream
{
    public:
        CPBufferOutputStream(){}
        ~CPBufferOutputStream(){}

        void Reset()
        {
            m_idx=0;
        }

        std::string ToString()
        {
            return std::string(m_szBuffer,m_idx);
        }

        virtual bool Next(void** data, int* size)
        {
            *data=m_szBuffer+m_idx;
            *size=sizeof(m_szBuffer)-m_idx;
            m_idx+=*size;
            return true;
        }
        virtual void BackUp(int count)
        {
            m_idx-=count;
        }

        virtual ::google::protobuf::int64 ByteCount() const
        {
            return m_idx;
        }

    private:
        ::google::protobuf::int64 m_idx;
        char m_szBuffer[PROTOBUFFER_MAX_BUFFER_SIZE];
};

使用并获取结果

CPBufferInputStream          m_pbinputstream;
CPBufferOutputStream         m_pboutputstream;

//将m_msg PB结构序列化、压缩并将结果传回 str_output 
bool Serialize(std::string* str_output) const
{
    CPBufferOutputStream   stream_output;
    ::google::protobuf::io::GzipOutputStream m_zip(stream_output);

    if(!m_msg.SerializeToZeroCopyStream(&m_zip))
    {
        return false;
    }

    if(!m_zip.Close())
    {
        return false;
    }

    *str_output=stream_output->ToString();
    return true;
}

//将 str_serial 反序列化成PB结构
bool Parse(const std::string& str_serial)
{
    CPBufferInputStream stream_input;
    stream_input->Reset((char*)str_serial.c_str(),(unsigned)str_serial.length());
    ::google::protobuf::io::GzipInputStream m_zip(&stream_input);
    if(!m_msg.ParseFromZeroCopyStream(&m_zip))
    {
        return false;
    }

    return true;
}

ProtoBuffer io 模块设计方法

  1. 在接口设计上,序列化函数需要用到的缓冲区可以是内部缓冲区和外部缓冲区,并没有做严格的限定。
  2. 当需要使用外部缓冲区的时候,PB定义了一个 ZeroCopyOutputStream 这样的一个输入流(这个和STL的IOStream有区别,请注意),指定了只需要Stream提供3个接口:Next(获取buffer地址),BackUp(返还buufer),ByteCount(查看一共使用了多少大小的buffer),接口小而完备。
  3. 压缩之后的序列化,主要是通过实现 ZeroCopyOutputStream 来进行,PB中已经实现了一个通过 GzipInputStream/GzipOutputStream 来进行实现,但是,GZipxxStream 这个接口仍然需要缓冲区,所以此时GzipxxxStream是作为一个适配器(或说是代理)来进行工作,使用时候需要提供一个 ZeroCopyOutputStream 的派生类来传入缓冲区。
  4. 简而言之,PB 在第一次序列化之后(未压缩数据),缓冲区放在 GzipxxxStream 的实现中(可以通过 GZipxxStream::Option()自己指定不指定则会new)。 而序列化完成之后,压缩时候使用的缓冲区,则有 ZeroCopyOutputStream 传入缓冲区来提供。

ascii 图表(测试显示效果)

ascii 图表

测试 ascii 图形在blog中的显示效果的文章
window chrome 下显示 ascii效果最好, mac 差一些。

英文框型

         +---+         
         |V7 |         
         +---+         
           |           
           v           
       +-------+       
       |  V1   |       
       +-------+       
         |  ||         
     -----  |--------  
     |      ---     |  
     v        |     |  
  +-----+     |     |  
  | V2  |     |     |  
  +-----+     |     |  
    | |       |     |  
  --- ---     |     |  
  |     |     |     |  
  v     v     v     v  
+---+ +---+ +---+ +---+
|V5 | |V6 | |V4 | |V3 |
+---+ +---+ +---+ +---+

中文框型

                         +--------+
                         |  顶框  |
                         +--+-----+
               +------------+----------------+
               |                             |
          +----+----+                    +---+---+
          |  左框1  |                    | 右框1 |
          +---------+                    +-------+

C语言

#include "stdio.h"
int main(int argc,char* argv[])
{
    printf("hello world");
    return 0;
}

C语言之二

#include <iostream>

int main(int argc, char *argv[]) {

  /* An annoying "Hello World" example */
  for (auto i = 0; i < 0xFFFF; i++)
    cout << "Hello, World!" << endl;

  char c = '\n';
  unordered_map <string, vector<string> > m;
  m["key"] = "\\\\"; // this is an error

  return -2e3 + 12l;
}

ascii表格

+-----------------------------------------------------------------------------+
|                   |                   |                   |                 |
|    title 1        |     title 2       |     title 3       |     title 4     |
|                   |                   |                   |                 |
+-------------------+-------------------+-------------------+-----------------+
|                   |                   |    bla bla        |                 |
| bla bla           |                   |    bla bla bla    |                 |
| bla bla bla       |    bla bla        |    bla bla bla ...| bla bla         |
| bla bla bla ...   |    bla bla bla    |                   | bla bla bla     |
|                   |    bla bla bla ...|                   | bla bla bla ... |
|                   |                   |                   |                 |
|                   |                   |                   |                 |
+-----------------------------------------------------------------------------+

ascii椭圆形


         ***********                  ***********
     ****           ****          ****           ****
  ***                   ***    ***                   ***
 *                         *  *                         *
*         ellipse           **        ellipse            *
*         ellipse...        **        ellipse...         *
*         ellipse..         **        ellipse..          *
 *                         *  *                         *
  ***                   ***    ***                   ***
     ****           ****          ****           ****
         ***********                  ***********

ascii 箭头线


                            -----                                             -----                       
                      -----/     \----                                   ----/     \----                  
                -----/                \----                         ----/               \----             
          -----/                           \----               ----/                         \----        
    -----/                                      \----     ----/                                   \----   
---/                                                 \---/                                             \->

小箭头

                  ^
                  |
           <------+------> 
                  |
                  v

大箭头

                  . 
                 /_\
                  |
                  |
         <|-------+--------|>
                  |
                 _|_
                 \ /
                  ' 

自由画(鼠标画人头)

           ************ 
          **********************
       ******  *************** ****  
     **********           *** ******* 
   *****                    ***    ** 
     **                       **      
     *                          *  
     *      *          *        ** 
     *                           * 
     *                           * 
     **                         ** 
      *                  *     **  
      **         *    ***     **  
       **        ******     ***  
         *                ***   
          ***            **   
            **         ***  
              ****   **                                                                       
                  ****  

自由画(复制人头)

                                                          ************              
                                                        **********************     
                                                     ******  *************** ****  
                                                   **********           *** *******
                                                 *****                    ***    **
                                                   **                       **     
                                                   *                          *    
                                                   *      *          *        **   
            ************                           *                           *   
           **********************                  *                           *   
        ******  *************** ****               **                         **   
      **********           *** *******              *                  *     **    
    *****                    ***    **              **         *    ***     **     
      **                       **                    **        ******     ***      
      *                          *                     *                ***        
      *      *          *        **                     ***            **          
      *                           *                       **         ***           
      *                           *                         ****   **              
      **                         **                             ****               
       *                  *     **                                                 
       **         *    ***            ************                                 
        **        ******     *       ********************                          
          *                ***    ******  *************** ****                     
           ***            **    **********           *** *******                   
             **         ***   *****                    ***    **                   
               ****   **        **                       **                        
                   ****         *                          *                       
                                *      *          *        **                      
                                *                           *                      
                                *                           *                      
                                **                         **                      
                                 *                  *     **                       
                                 **         *    ***     **                        
                                  **        ******     ***                         
                                    *                ***                           
                                     ***            **                             
                                       **         ***                              
                                         ****   **                                 
                                             ****                                  

graphviz

digraph example3 {
Server1 -> Server2
Server2 -> Server3
Server3 -> Server1

Server1 [shape=box, label="Server1\nWeb Server", fillcolor="#ABACBA", style=filled]
Server2 [shape=triangle, label="Server2\nApp Server", fillcolor="#DDBCBC", style=filled]
Server3 [shape=circle, label="Server3\nDatabase Server", fillcolor="#FFAA22",style=filled]
}

文件系统缓存调优检查表(性能之巅-内存部分笔记)

目标

为你的操作系统开发出以下的步骤:

  1. 一个文件系统调优检查表。这应该列出文件系统缓存,以及如何检查他们的大小、使用情况和命中率;
  2. 一个文件系统负载特征归纳表。包括如何得到各项详细信息,并优先使用系统现有的文件系统观察工具。

文件系统调优检查表

文件系统的缓存 表 3.2 磁盘I/O缓存层级示例

缓存 实例
应用程序缓存 apache/gameserver
服务器缓存 memcached/redis
数据库缓存 mysql 缓存
目录缓存 DNLC
文件元数据缓存 inode缓存
操作系统缓存区高速缓存 segvn
文件系统主缓存 ZFS ARC
文件系统次缓存 ZFS L2ARC
设备缓存 ZFS vdev
块设备 缓存区高速缓存
磁盘控制器缓存 RAID卡缓存
存储阵列缓存 -
磁盘内置缓存 -

Linux-文件系统缓存

cache && buffer

free, ps,top,vmstat 命令查看,主要是通过 /proc/meminfo 算出来的,其中 used 字段,是通过 used=total-free 得到,是个间接值。

[LawrenceChi@LOCAL-96-156 ~]$ free ;top -b -n 1 | grep -e "Mem:" -e "Swap:"; vmstat 1 5;vmstat -a 1 5;
             total       used       free     shared    buffers     cached
Mem:       8062400    6961432    1100968          0     390780    3838792
-/+ buffers/cache:    2731860    5330540
Swap:      2097144      55724    2041420
Mem:   8062400k total,  6961944k used,  1100456k free,   390780k buffers
Swap:  2097144k total,    55724k used,  2041420k free,  3838796k cached
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0  55724 1100952 390780 3838796    0    0     0    12    0    4  4  1 95  0  0
 0  0  55724 1100960 390784 3838796    0    0     0    88  490  556  0  0 99  0  0
 0  0  55724 1100960 390784 3838796    0    0     0     0  537  570  0  0 99  0  0
 0  0  55724 1100836 390784 3838928    0    0     0     0 1298  833  2  1 97  0  0
 0  0  55724 1100868 390784 3838960    0    0     0     0  535  570  1  1 99  0  0
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu-----
 r  b   swpd   free  inact active   si   so    bi    bo   in   cs us sy id wa st
 1  0  55724 1100868 2023192 4141020    0    0     0    12    0    4  4  1 95  0  0
 0  0  55724 1101496 2023228 4140004    0    0     0    16  861  654  1  1 98  0  0
 0  0  55724 1101496 2023300 4140048    0    0     0   180 1085  767  1  1 98  0  0
 0  0  55724 1101496 2023336 4140056    0    0     0     0  611  558  1  1 99  0  0
 0  0  55724 1101380 2023444 4140076    0    0     0     0 1215  781  2  1 97  0  0
[LawrenceChi@LOCAL-96-156 ~]$

cache 是文件系统读缓存。
buffer是文件系统写缓存,通过查看 /proc/meminfo 中的Dirty,查看有多少“脏页”。

文件是否已经在缓存中及处理

文件系统读缓存,可以通过vmtouch工具来检查文件是否已经在缓存中。

vmtouch 判断文件是否在缓存中原理

  1. fopen 需要检测的文件,mmap到内存;
  2. 通过 mincore 系统调用,判断文件页是否已经在内存中;
  3. ummap,输出统计结果。
  4. 可以按照文件、文件夹进行统计;

vmtouoch 将文件加载到缓存中的原理

  1. fopen 需要检测的文件,mmap到内存;
  2. 每一个页(page)访问一次,触发文件缓存机制;
  3. 退出;

vmtouch 将文件从缓存中释放掉的原理

  1. fopen 文件, mmap到内存;
  2. posix_fadvise 系统调用 进行内存释放。 关于 posix_fadvise不应定真正释放内存,这里有一篇文章说明文件脏数据的对内存释放的影响;
  3. ummap, flcose。

缓存占用空间

cat /proc/meminfo 中 Buffers、Cached可以看到文件系统缓存占用情况:

[LawrenceChi@LOCAL-96-157 gameserver]$ cat /proc/meminfo
MemTotal:        8062400 kB
MemFree:          201160 kB
Buffers:          294268 kB
Cached:          3373396 kB
SwapCached:        83540 kB
Active:          3994004 kB
Inactive:        2531980 kB
Active(anon):    1891580 kB
Inactive(anon):   968060 kB
Active(file):    2102424 kB
Inactive(file):  1563920 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:       2097144 kB
SwapFree:         824900 kB
Dirty:               232 kB
Writeback:             0 kB
AnonPages:       2775564 kB
Mapped:            62636 kB
Shmem:              1308 kB
Slab:            1030904 kB
SReclaimable:     878300 kB
SUnreclaim:       152604 kB
KernelStack:       16280 kB
PageTables:       161188 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     6128344 kB
Committed_AS:   19296144 kB
VmallocTotal:   34359738367 kB
VmallocUsed:      159412 kB
VmallocChunk:   34359571032 kB
HardwareCorrupted:     0 kB
AnonHugePages:   1036288 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:       10240 kB
DirectMap2M:     8378368 kB

vmstat命令中关于内存、缓存的选项说明

vmstat -m

The -m switch displays slabinfo.
释放页面缓存,把那个比较连个 vmstat 得到变少的数据统计。
buffer_head/radix_tree_node:这是页面缓存

Free pagecache only use following command
# sync; echo 1 > /proc/sys/vm/drop_caches
lawrencechi@LawrencechideMacBook-Pro ~/Downloads » cat clean.1.txt| awk '{ if($7<$2){b=$2-$7; printf("%s, after-befor= %d\n",$0,b);}}'  | sort -t"=" -k 2 -n -r | head -n 10
buffer_head              969164 970584    104     37               buffer_head                8500  59977    104     37               , after-befor= 960664
radix_tree_node           38661  40180    560      7               radix_tree_node            4418  14392    560      7               , after-befor= 34243
filp                      21109  24260    192     20               filp                      20890  24260    192     20               , after-befor= 219
cred_jar                   1809   2620    192     20               cred_jar                   1705   2620    192     20               , after-befor= 104
proc_inode_cache         111179 111558    656      6               proc_inode_cache         111122 111558    656      6               , after-befor= 57
size-32                   19411  19600     32    112               size-32                   19356  19600     32    112               , after-befor= 55
size-128                   3420   3630    128     30               size-128                   3367   3630    128     30               , after-befor= 53
anon_vma_chain            93932 102025     48     77               anon_vma_chain            93884 102025     48     77               , after-befor= 48
size-1024                  2048   2384   1024      4               size-1024                  2004   2384   1024      4               , after-befor= 44
task_delay_info            1029   1292    112     34               task_delay_info             990   1292    112     34               , after-befor= 39
Free dentries and inodes use following command
# sync; echo 2 > /proc/sys/vm/drop_caches

这次得到的结果是:dentry,pro_inode_cache,ext4_inode_cache

lawrencechi@LawrencechideMacBook-Pro ~/Downloads » cat clean.2.txt| awk '{ if($7<$2){b=$2-$7; printf("%s, after-befor= %d\n",$0,b);}}'  | sort -t"=" -k 2 -n -r | head -n 15
dentry                   1392310 1392420    192     20  dentry                    14288  81380    192     20        , after-befor= 1378022
buffer_head              969164 970584    104     37    buffer_head               11576  59718    104     37    , after-befor= 957588
proc_inode_cache         111179 111558    656      6    proc_inode_cache             50    168    656      6    , after-befor= 111129
ext4_inode_cache          99263  99296   1016      4    ext4_inode_cache           1187   2644   1016      4    , after-befor= 98076
radix_tree_node           38661  40180    560      7    radix_tree_node            4685  14091    560      7    , after-befor= 33976
size-64                   44416  44840     64     59    size-64                   12858  41772     64     59    , after-befor= 31558
inode_cache                6530   6588    592      6    inode_cache                4266   4488    592      6    , after-befor= 2264
size-128                   3420   3630    128     30    size-128                   2013   3450    128     30    , after-befor= 1407
anon_vma_chain            93932 102025     48     77    anon_vma_chain            93401 102025     48     77    , after-befor= 531
vm_area_struct           101400 110903    200     19    vm_area_struct           101000 110903    200     19    , after-befor= 400
anon_vma                  54374  59340     40     92    anon_vma                  54095  59340     40     92    , after-befor= 279
filp                      21109  24260    192     20    filp                      20854  24260    192     20    , after-befor= 255
pid                        1324   1560    128     30    pid                        1245   1560    128     30    , after-befor= 79
fs_cache                    629    944     64     59    fs_cache                    563    944     64     59    , after-befor= 66
task_delay_info            1029   1292    112     34    task_delay_info             969   1292    112     34    , after-befor= 60
Free pagecache, dentries and inodes in cache memory# sync; echo 3 > /proc/sys/vm/drop_caches

结论,在使用 vmstat -m进行分析内存缓存的时候,我们最重要关注是:
buffer_head/radix_tree_node/dentry,pro_inode_cache,ext4_inode_cache

缓存项 说明
buffer_head 每个buffer_head管理的单元是内存页中对应的一个磁盘块,也就是说buffer_head管理单元是 “页”的一个子集. buffer_head理解、解析
radix_tree_node radix树(基数树)数据缓存.Linux 内核数据结构:Radix树 Radix tree(wiki)
dentry 高速目录缓存
pro_inode_cache /proc 文件系统缓存,可以侧面反映进程开启情况
ext4_inode_cache NULL

cache 命中率计算

Linux Page Cache Hit Ratio

vmstat -s

The -s switch displays a table of various event counters and memory statistics. This display does not repeat.

[LawrenceChi@LOCAL-96-157 ~]$ vmstat -s
8062400  total memory
7753332  used memory
4019728  active memory
2819196  inactive memory
309068  free memory
314848  buffer memory
3536000  swap cache
2097144  total swap
1106356  used swap
990788  free swap
30702361 non-nice user cpu ticks
12601 nice user cpu ticks
21383073 system cpu ticks
2601397816 idle cpu ticks
4037027 IO-wait cpu ticks
15937 IRQ cpu ticks
1583247 softirq cpu ticks
0 stolen cpu ticks
106072566 pages paged in
405098416 pages paged out
685075 pages swapped in
2390879 pages swapped out
495740634 interrupts
2065133133 CPU context switches
1457495157 boot time
13714053 forks
[LawrenceChi@LOCAL-96-157 ~]$

ssh通过nc代理连接远程window机器上的虚拟机

解决问题

场景如下:

  1. window拥有内网的ip(172.20.133.143);
  2. window上的centos虚拟机没有内网IP,但是拥有window主机给他的IP(192.168.56.101);
  3. MBP 拥有内网IP(172.30.203.17)。

想要达成目标: MBP上的iterm2终端能够连接到window上的虚拟机

解决办法

  1. window上使用 Charles ,创建 socket (proto: SOCKS5)代理;
  2. 在MBP的iterm2上使用 ssh 登陆,但是通过nc走socket过去;

典型配置

  1. 虚拟机配置:
    虚拟机配置

2.Charles 配置:
Charles 配置

3.iterm2 命令

ssh chiyl@192.168.56.101 -o "ProxyCommand=nc -X 5 -x172.20.133.143:8889 192.168.56.101 3600"
scp -o "ProxyCommand=nc -X 5 -x172.20.133.143:8889 192.168.56.101 3600" -P 3600 template.bat.bak chiyl@192.168.56.101:~/

参考

Connect with SSH through a proxy


stl auto_ptr源码注释及若干问题笔记

解决问题

1.注释 stl auto_ptr 源码。
2.弄明白 auto_ptr_ref 实现机制和要解决的问题;
3.自己实现一个auto_ptr,并实现一个测试程序;

stl auto_ptr 源码注释

{
    /**
     *  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.
     */
    //注释:当函数的返回值是 auto_ptr 时候,会造成一次传值转换。但是 auto_ptr构造
    //时候,需要将传入的auto_ptr进行release(),因此无法以传值方式进行构造,必须是引用
    //如: auto_ptr(auto_ptr& _a)。这就造成了函数返回值是传值时候会编译出错。
    //出错场景:
    //1. auto_ptr<int> p=new auto_ptr<int>(new int);
    //2. auto_ptr<int> func1();
    //   auto_ptr<int> p=func1();
    //
    //解决方案是利用返回时候进行的一次强行值转换进行。
    //1. return 时候,先强行将 auto_ptr 强行转换成 auto_ptr_ref;
    //2. auto_ptr 增加 auto_ptr(auto_ptr_ref _ref) 构造实现。
    //3. auto_ptr 增加一个操作符 operator = (auto_ptr_ref) 的实现。
    //4. 至此,实现了对返回值是 auto_ptr 的值的函数调用;
    template<typename _Tp1>
        struct auto_ptr_ref
        {
            _Tp1* _M_ptr;

            explicit
                auto_ptr_ref(_Tp1* __p): _M_ptr(__p) { }
        } _GLIBCXX_DEPRECATED;

    /**
     *  @brief  A simple smart pointer providing strict ownership semantics.
     *
     *  The Standard says:
     *  <pre>
     *  An @c auto_ptr owns the object it holds a pointer to.  Copying
     *  an @c auto_ptr copies the pointer and transfers ownership to the
     *  destination.  If more than one @c auto_ptr owns the same object
     *  at the same time the behavior of the program is undefined.
     *
     *  The uses of @c auto_ptr include providing temporary
     *  exception-safety for dynamically allocated memory, passing
     *  ownership of dynamically allocated memory to a function, and
     *  returning dynamically allocated memory from a function.  @c
     *  auto_ptr does not meet the CopyConstructible and Assignable
     *  requirements for Standard Library <a
     *  href="tables.html#65">container</a> elements and thus
     *  instantiating a Standard Library container with an @c auto_ptr
     *  results in undefined behavior.
     *  </pre>
     *  Quoted from [20.4.5]/3.
     *
     *  Good examples of what can and cannot be done with auto_ptr can
     *  be found in the libstdc++ testsuite.
     *
     *  _GLIBCXX_RESOLVE_LIB_DEFECTS
     *  127.  auto_ptr<> conversion issues
     *  These resolutions have all been incorporated.
     */
    template<typename _Tp>
        class auto_ptr
        {
            private:
                //保存指针的数据
                _Tp* _M_ptr;

            public:
                /// The pointed-to type.
                //也许是为了增加可读性
                //手册中规定了 auto_ptr::element_type 是类型别名
                typedef _Tp element_type;

                /**
                 *  @brief  An %auto_ptr is usually constructed from a raw pointer.
                 *  @param  __p  A pointer (defaults to NULL).
                 *
                 *  This object now @e owns the object pointed to by @a __p.
                 */
                //explicit 这个限制符让强行转换失效,类如: auto_ptr<int> = 0x10001; 
                //指针 0x10001 不再构造 auto_ptr(*p) 并调用 operator =;
                explicit
                    auto_ptr(element_type* __p = 0) throw() : _M_ptr(__p) { }

                /**
                 *  @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 ownership.
                 */
                //另一个相同类型的 auto_ptr 作为构造参数;
                auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) { }

                /**
                 *  @brief  An %auto_ptr can be constructed from another %auto_ptr.
                 *  @param  __a  Another %auto_ptr of a different but related type.
                 *
                 *  A pointer-to-Tp1 must be convertible to a
                 *  pointer-to-Tp/element_type.
                 *
                 *  This object now @e owns the object previously owned by @a __a,
                 *  which has given up ownership.
                 */
                //另一个不同类型的 auto_ptr 作为构造参数;
                template<typename _Tp1>
                    auto_ptr(auto_ptr<_Tp1>& __a) throw() : _M_ptr(__a.release()) { }

                /**
                 *  @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 ownership.  The object that this one @e
                 *  used to own and track has been deleted.
                 */
                //相同类型的 auto_ptr 赋值
                //赋值的时候,先将右值 auto_ptr 保存的地址释放掉,
                //左值 auto_ptr 赋值,注意返回是自己的一个引用。
                auto_ptr&
                    operator=(auto_ptr& __a) throw()
                    {
                        reset(__a.release());
                        return *this;
                    }

                /**
                 *  @brief  %auto_ptr assignment operator.
                 *  @param  __a  Another %auto_ptr of a different but related type.
                 *
                 *  A pointer-to-Tp1 must be convertible to a pointer-to-Tp/element_type.
                 *
                 *  This object now @e owns the object previously owned by @a __a,
                 *  which has given up ownership.  The object that this one @e
                 *  used to own and track has been deleted.
                 */
                //不相同类型的 auto_ptr 赋值
                //赋值的时候,先将右值 auto_ptr 保存的地址释放掉,
                //左值 auto_ptr 赋值,注意返回是自己的一个引用。
                template<typename _Tp1>
                    auto_ptr&
                    operator=(auto_ptr<_Tp1>& __a) throw()
                    {
                        reset(__a.release());
                        return *this;
                    }

                /**
                 *  When the %auto_ptr goes out of scope, the object it owns is
                 *  deleted.  If it no longer owns anything (i.e., @c get() is
                 *  @c NULL), then this has no effect.
                 *
                 *  The C++ standard says there is supposed to be an empty throw
                 *  specification here, but omitting it is standard conforming.  Its
                 *  presence can be detected only if _Tp::~_Tp() throws, but this is
                 *  prohibited.  [17.4.3.6]/2
                 */
                //智能指针析构时候,释放自己拥有的申请的内存
                ~auto_ptr() { delete _M_ptr; }

                /**
                 *  @brief  Smart pointer dereferencing.
                 *
                 *  If this %auto_ptr no longer owns anything, then this
                 *  operation will crash.  (For a smart pointer, <em>no longer owns
                 *  anything</em> is the same as being a null pointer, and you know
                 *  what happens when you dereference one of those...)
                 */
                //智能指针提领返回类型的引用
                //注意,如果这里如果智能指针是空值,程序会挂。
                //这里的const是指不能修改指针,对于指针指向的对象,是可以修改的。
                element_type&
                    operator*() const throw() 
                    {
                        _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
                        return *_M_ptr; 
                    }

                /**
                 *  @brief  Smart pointer dereferencing.
                 *
                 *  This returns the pointer itself, which the language then will
                 *  automatically cause to be dereferenced.
                 */
                //操作符 -> 会返回指针
                element_type*
                    operator->() const throw() 
                    {
                        _GLIBCXX_DEBUG_ASSERT(_M_ptr != 0);
                        return _M_ptr; 
                    }

                /**
                 *  @brief  Bypassing the smart pointer.
                 *  @return  The raw pointer being managed.
                 *
                 *  You can get a copy of the pointer that this object owns, for
                 *  situations such as passing to a function which only accepts
                 *  a raw pointer.
                 *
                 *  @note  This %auto_ptr still owns the memory.
                 */
                //返回原始指针,有一些函数可能使用原始指针,此时需要用到 get()
                element_type*
                    get() const throw() { return _M_ptr; }

                /**
                 *  @brief  Bypassing the smart pointer.
                 *  @return  The raw pointer being managed.
                 *
                 *  You can get a copy of the pointer that this object owns, for
                 *  situations such as passing to a function which only accepts
                 *  a raw pointer.
                 *
                 *  @note  This %auto_ptr no longer owns the memory.  When this object
                 *  goes out of scope, nothing will happen.
                 */
                //放弃掉对 _M_ptr 的访问权限
                element_type*
                    release() throw()
                    {
                        element_type* __tmp = _M_ptr;
                        _M_ptr = 0;
                        return __tmp;
                    }

                /**
                 *  @brief  Forcibly deletes the managed object.
                 *  @param  __p  A pointer (defaults to NULL).
                 *
                 *  This object now @e owns the object pointed to by @a __p.  The
                 *  previous object has been deleted.
                 */
                //使用指针 p 对 _M_ptr 进行赋值
                void reset(element_type* __p = 0) throw()
                    {
                        if (__p != _M_ptr)
                        {
                            delete _M_ptr;
                            _M_ptr = __p;
                        }
                    }

                /** 
                 *  @brief  Automatic conversions
                 *
                 *  These operations convert an %auto_ptr into and from an auto_ptr_ref
                 *  automatically as needed.  This allows constructs such as
                 *  @code
                 *    auto_ptr<Derived>  func_returning_auto_ptr(.....);
                 *    ...
                 *    auto_ptr<Base> ptr = func_returning_auto_ptr(.....);
                 *  @endcode
                 */
                //使用见 auto_ptr_ref 的设计方法
                //使用 auto_ptr_ref 构造 auto_ptr
                auto_ptr(auto_ptr_ref<element_type> __ref) throw()
                    : _M_ptr(__ref._M_ptr) { }

                //使用见 auto_ptr_ref 的设计方法
                //使用 auto_ptr_ref 复制
                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;
                    }

                //使用见 auto_ptr_ref 的设计方法,
                //类型强行转换,将自己(auto_ptr)强行转成 auto_ptr_ref;
                //转换之后,自己不再拥有内存
                template<typename _Tp1>
                    operator auto_ptr_ref<_Tp1>() throw()
                    { return auto_ptr_ref<_Tp1>(this->release()); }

                //类型强行转换,将自己(auto_ptr)强行转成另一个类型的auto_ptr;
                //转换之后,自己不再拥有内存
                //这个在什么场景下调用? 有些费解。
                //TODO:解析这个函数应用场景
                template<typename _Tp1>
                    operator auto_ptr<_Tp1>() throw()
                    { return auto_ptr<_Tp1>(this->release()); }
        } _GLIBCXX_DEPRECATED;

    // _GLIBCXX_RESOLVE_LIB_DEFECTS
    // 541. shared_ptr template assignment and void
    // 对空类型的  auto_ptr 偏特化,如果不做处理, auto_ptr<void> p4; 会出错
    template<>
        class auto_ptr<void>
        {
            public:
                //手册中规定了 auto_ptr::element_type 是类型别名
                typedef void element_type;
        } _GLIBCXX_DEPRECATED;

    //C++ 11 宏
#if __cplusplus >= 201103L
    template<_Lock_policy _Lp>
        template<typename _Tp>
        inline
        __shared_count<_Lp>::__shared_count(std::auto_ptr<_Tp>&& __r)
        : _M_pi(new _Sp_counted_ptr<_Tp*, _Lp>(__r.get()))
        { __r.release(); }

    template<typename _Tp, _Lock_policy _Lp>
        template<typename _Tp1>
        inline
        __shared_ptr<_Tp, _Lp>::__shared_ptr(std::auto_ptr<_Tp1>&& __r)
        : _M_ptr(__r.get()), _M_refcount()
        {
            __glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
                static_assert( sizeof(_Tp1) > 0, "incomplete type" );
            _Tp1* __tmp = __r.get();
            _M_refcount = __shared_count<_Lp>(std::move(__r));
            __enable_shared_from_this_helper(_M_refcount, __tmp, __tmp);
        }

    //C++ 新增 share_ptr
    //gcc.4.9.3/gcc-4.9.3/libstdc++-v3/include/bits/shared_ptr_base.h
    template<typename _Tp>
        template<typename _Tp1>
        inline
        shared_ptr<_Tp>::shared_ptr(std::auto_ptr<_Tp1>&& __r)
        : __shared_ptr<_Tp>(std::move(__r)) { }

    //C++ 新增 unique_ptr,关于 unique_ptr 在文件
    //gcc.4.9.3/gcc-4.9.3/libstdc++-v3/include/bits/unique_ptr.h
    template<typename _Tp, typename _Dp>
        template<typename _Up, typename>
        inline
        unique_ptr<_Tp, _Dp>::unique_ptr(auto_ptr<_Up>&& __u) noexcept
        : _M_t(__u.release(), deleter_type()) { }
#endif

    _GLIBCXX_END_NAMESPACE_VERSION
} // namespace

auto_ptr_ref 实现机制

auto_ptr_ref 是用来解决 auto_ptr::auto_ptr(const auto_ptr&);
类似这种场景:

auto_ptr function();
int main(){auto_ptr f=function();}

注意到,这个 function 的返回值是一个临时变量(这个匿名的变量会在这个语句结束之后自动释放(自动析构)。C++语法规定,临时变量传输必须是 const 引用或者传值.这样做是为了避免程序员修改一个即将被销毁的变量。

那如何根据 function 的返回值来生成一个 'f' 呢?答案是通过利用 auto_ptr_ref 类。 C++编译器通过以下两个过程达成目标。
1.将 auto_ptr 转成一个 auto_ptr_ref 对象, 这时候 auto_ptr 将自身的指向的内存传给 auto_ptr_ref.(注意,这一步我们是通过调用一个 no-const 强行转换函数临时生成).

  1. 将 auto_ptr_ref 转成 auto_ptr, 这时候的 auto_ptr 就是我们所需要的'f1',并获得 auto_ptr_ref 指向的内存。

这里面的返回值优化和函数中的const 参数调用重载另外说明

下面的 function1(),function2() 可以清楚的观察到这个过程。

#include <iostream>
using std::cout;
using std::endl;

class auto_ptr
{
    private:
        struct auto_ptr_ref { };

    public:
        auto_ptr() { cout << "auto_ptr::auto_ptr()\n"; }
        auto_ptr(auto_ptr&) { cout << "auto_ptr::auto_ptr(auto_ptr&)\n"; }
        operator auto_ptr_ref()
        { cout << "auto_ptr::operator auto_ptr_ref()\n"; }
        auto_ptr(auto_ptr_ref) { cout <<
            "auto_ptr::auto_ptr(auto_ptr_ref)\n"; }
        ~auto_ptr() { cout << "auto_ptr::~auto_ptr()\n"; }
};

auto_ptr function1();
auto_ptr function2();

int main()
{
    auto_ptr f1=function1(); // 调用 function function1() ,过程如下
    // 调用 "auto_ptr::auto_ptr(const auto_ptr&)", 没找到
    // 不能调用"auto_ptr::auto_ptr(auto_ptr&)",因为function1()的返回值是一个临时变量。
    // 编译器决定执行“两步转换”:
    // 1) 使用 "auto_ptr::operator auto_ptr_ref()"
    // 2) 使用 "auto_ptr::auto_ptr(auto_ptr_ref)" 构造出 'f1'
    // 此时,使用 "auto_ptr::~auto_ptr()" 析构销毁 'function1()' 的返回值

    cout << "after1\n";

    auto_ptr f2=function2(); //返回值步骤具体和function1()类似,但是里面还生成了一个匿名函数,因此会多一次 “两步转换”;
    cout << "after2\n";
} // 2 calls to "auto_ptr::~auto_ptr()" to destroy 'f1' and 'f2'

auto_ptr function1() //编译器**如果没执行返回值优化**,其调用如下所示
{
    auto_ptr a; // 调用"auto_ptr::auto_ptr()",生成变量 a
    return a; // 调用"auto_ptr::auto_ptr(auto_ptr&)" 生成临时变量返回。
} // 调用"auto_ptr::~auto_ptr()" 销毁变量 'a'

auto_ptr function2() 
{
    return auto_ptr(); //调用"auto_ptr::auto_ptr()"生成临时变量。
    //调用 "auto_ptr::auto_ptr(const auto_ptr&)",没找到
    //编译器质性“两步转换”,生成一个临时变量用于返回。
}
//调用"auto_ptr::~auto_ptr()" 销毁返回的临时变量。

lawrencechi@LawrencechideMacBook-Pro ~/temp/test » make                                                                                                                        127 ↵
g++ -g main.cpp -o test
main.cpp:14:58: warning: control reaches end of non-void function [-Wreturn-type]
        { cout << "auto_ptr::operator auto_ptr_ref()\n"; }
                                                         ^
1 warning generated.
lawrencechi@LawrencechideMacBook-Pro ~/temp/test » ./test
auto_ptr::auto_ptr()
auto_ptr::operator auto_ptr_ref()
auto_ptr::auto_ptr(auto_ptr_ref)
auto_ptr::~auto_ptr()
after1
auto_ptr::auto_ptr()
auto_ptr::operator auto_ptr_ref()
auto_ptr::auto_ptr(auto_ptr_ref)
auto_ptr::~auto_ptr()
auto_ptr::operator auto_ptr_ref()
auto_ptr::auto_ptr(auto_ptr_ref)
auto_ptr::~auto_ptr()
after2
auto_ptr::~auto_ptr()
auto_ptr::~auto_ptr()
lawrencechi@LawrencechideMacBook-Pro ~/temp/test »

自己实现 auto_ptr

1.考虑构造函数 auto_ptr(const auto_ptr&) 不能用,必须用 auto_ptr(auto_ptr&);
2.由于1,所以必须使用 auto_ptr_ref机制规避返回值为auto_ptr时候的问题;
3.必须要考虑特化版本 template<> auto_ptr;
4.赋值操作符,构造函数考虑类型转换问题,构造函数中的 explicit 限定符问题;

#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;

template<typename T>
struct auto_ptr_ref
{
    T* pointee;

    explicit
        auto_ptr_ref(T* p): pointee(p) {}
};

template <class T>
class my_auto_ptr
{
    public:
        explicit my_auto_ptr(T* p=NULL):pointee(p) {
            cout<<"my_auto_ptr(T* p=NULL)"<<pointee<<endl;
        }
        template <class U>
            my_auto_ptr(my_auto_ptr<U>& rhs):pointee(rhs.release()){
                cout<<"my_auto_ptr(my_auto_ptr<U>& rhs)"<<pointee<<endl;
            }
        ~my_auto_ptr()
        {
            cout<<"~my_auto_ptr():"<<pointee<<endl;
            if(pointee)
            {
                delete pointee;
                pointee=NULL;
            }
        }

        T* release()
        {
            T* tmp=this->pointee;

            this->pointee=NULL;
            return tmp;
        }

        template<class U>
        void reset(U* rp)
        {
            if(pointee!=rp)
            {
                delete this->pointee;
                pointee=rp;
            }
        }

        T& operator*()const{ return *pointee;}
        T* operator->()const{ return pointee;}
        T* get() const {return pointee;}

        template <class U>
            my_auto_ptr<T>& operator=(my_auto_ptr<U>& rhs)
            {
                reset(rhs.release());
                return *this;
            }

        my_auto_ptr& operator=(my_auto_ptr& rhs)
        {
            reset(rhs.release());
            return *this;
        }

        my_auto_ptr(auto_ptr_ref<T> __ref): pointee(__ref.pointee) {
            cout<<"my_auto_ptr(auto_ptr_ref<T> __ref)"<<endl;
        }

        my_auto_ptr&
            operator=(auto_ptr_ref<T> __ref)
            {
                cout<<"my_auto_ptr& operator=(auto_ptr_ref<T> __ref)"<<endl;

                if (__ref.pointee!= pointee)
                {
                    delete pointee;
                    pointee= __ref.pointee;
                }
                return *this;
            }

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

        template<typename _Tp1>
            operator my_auto_ptr<_Tp1>() throw()
            { 
                cout<<"template<typename _Tp1>operator my_auto_ptr<_Tp1>() throw()"<<endl;
                return my_auto_ptr<_Tp1>(this->release());
            }

    private:
        T* pointee;
};

template<> 
class my_auto_ptr<void>
{
    public:
        typedef void element_type;
};

//测试自动销毁
void func()
{
    cout<<"func"<<endl;
    my_auto_ptr<string> ps(new string("test"));
    cout << *ps <<endl;
    cout<< ps->size()<<endl;
}

//测试返回为 my_auto_ptr<int>
my_auto_ptr<int> func1()
{
    my_auto_ptr<int> a(new int);
    *a=12;

    return a;
}

int main()
{
    my_auto_ptr<int> p;
    my_auto_ptr<int> p2;
    my_auto_ptr<int> p3;
    my_auto_ptr<void> p4; //test void
    my_auto_ptr<void> p5;

    std::cout << "before new" <<std::endl;
    p = my_auto_ptr<int> (new int);
    std::cout << "after new" <<std::endl;

    std::cout << "before func" <<std::endl;
    func();
    std::cout << "after func" <<std::endl;

    std::cout << "before func2" <<std::endl;
    p2=func1(); 
    std::cout << "after func2" <<std::endl;

    *p = 11;
    p2 = p;
    std::cout << "p2 points to " << *p2 << '\n';

    p4=p5;

    return 0;
}

lawrencechi@LawrencechideMacBook-Pro ~/temp/test » g++ my_auto_ptr.cpp
lawrencechi@LawrencechideMacBook-Pro ~/temp/test » ./a.out
my_auto_ptr(T* p=NULL)0x0
my_auto_ptr(T* p=NULL)0x0
my_auto_ptr(T* p=NULL)0x0
before new
my_auto_ptr(T* p=NULL)0x7fdd6bd00000
template<typename _Tp1> operator auto_ptr_ref<_Tp1>() throw()
my_auto_ptr& operator=(auto_ptr_ref<T> __ref)
~my_auto_ptr():0x0
after new
before func
func
my_auto_ptr(T* p=NULL)0x7fdd6bd00010
test
4
~my_auto_ptr():0x7fdd6bd00010
after func
before func2
my_auto_ptr(T* p=NULL)0x7fdd6bd00030
template<typename _Tp1> operator auto_ptr_ref<_Tp1>() throw()
my_auto_ptr& operator=(auto_ptr_ref<T> __ref)
~my_auto_ptr():0x0
after func2
p2 points to 11
~my_auto_ptr():0x0
~my_auto_ptr():0x7fdd6bd00000
~my_auto_ptr():0x0

参考资料

http://cpptips.com/auto_ptr_ref