C++智指针之——boost::intrusive_ptr,一种与shared_ptr、unique_ptr截然不同的智能指针

智能指针boost::shared_ptr/std::shared_ptr使用了和数据无关的引用计数,在使用shared_ptr之前,必须要搞清楚资源的所有权和资源的使用权这两个问题(详见《C++代码整洁之道》一书关于智能指针的讲解)。

shared_ptr带来几个问题:

  • 引用计数要在shared_ptr对象间共享,所以它只能位于堆上。这使得每次都需要重新new一小块内存。这导致性能问题(当然,可以配合std::make_shared<>()和std::enable_shared_from_this<>来解决,std::enable_shared_from_this的使用一定要特别注意,很容易入坑,注意事项不多说了)
  • 引用计数的内存区域和数据区域不一致,缓存失效导致性能问题。
  • 编写代码不善,将导致同一个数据,绑定到了两个引用计数,从而导致双重删除问题。典型代码如下:
int* test = new int;
std::shared_ptr<int> x1 = test;
std::shared_ptr<int> x2 = test;

上面的代码,在释放test资源时,一定会出错——双重删除;

为了解决shared_ptr带来的这些问题,boost已经引入了另一个智能指针——boost::intrusive_ptr<>,不过这个指针用起来没有那么简单,是一种“侵入式”的智能指针。

先看用法,然后再讲实现原理。

简单的demo:

demo.h:

#pragma once

#include <iostream>
#include <string>

#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>

/**
 * @brief demo
 */
class demo final:public boost::intrusive_ref_counter<demo,boost::thread_safe_counter>
{
public:
    demo(const char *id):m_id(id)
    {
        std::cout<<m_id<<" ctor"<<std::endl;
    }

    ~demo()
    {
        std::cout<<m_id<<" dtor"<<std::endl;
    }
 
    demo(const demo &)=delete;
    demo& operator=(const demo &)=delete;
 
    demo(demo &&)=default;
    demo& operator=(demo &&)=default;

    boost::intrusive_ptr<demo> self();
 
private:
    std::string m_id;
};

using demoIPtr=boost::intrusive_ptr<demo>;

demo.cpp:

#include "demo.h"
#include "boost/smart_ptr/intrusive_ptr.hpp"

boost::intrusive_ptr<demo> demo::self()
{
    return boost::intrusive_ptr<demo>(this);
}

main.cpp:

#include <iostream>
#include <new>
#include <string>

#include <gflags/gflags.h>

#include <boost/smart_ptr/intrusive_ptr.hpp>

#include "demo.h"

DEFINE_string(test,"","use gflags test");

int main(int argc, char *argv[])
{
    gflags::SetUsageMessage("Usage");
    gflags::ParseCommandLineFlags(&argc,&argv,true);

    demoIPtr demoptr=boost::intrusive_ptr<demo>(new(std::nothrow) demo("first"));//will be destoried automaticly
    demo *pdemo=new(std::nothrow) demo("second");//will be destoried automaticly

    auto ptr=pdemo->self();
    auto ptr2=pdemo->self();

    auto ptr3=boost::intrusive_ptr<demo>(pdemo);// It is right
    auto ptr4=boost::intrusive_ptr<demo>(pdemo);// It is right
    auto ptr5=boost::intrusive_ptr<demo>(pdemo);// It is right,this is the main difference between intrusive_ptr and shared_ptr 

    auto ptrx=demoptr->self();

    
    return 0;
}

编译文件,CMakeLists.txt:

cmake_minimum_required( VERSION 3.8 FATAL_ERROR)
project(main LANGUAGES CXX)
 
#set dirs
list(APPEND CMAKE_MODULE_PATH 
    $ENV{BOOST_ROOT}/stage/lib/cmake  # append boost cmake dir
    /usr/local/lib/cmake/             # append gflags cmake dir
    )

set(PROJECT_ROOT ${CMAKE_CURRENT_LIST_DIR})
message("project dir:${PROJECT_ROOT}")
 
SET(BIN_DESTINATION ${PROJECT_SOURCE_DIR}/bin)
SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${BIN_DESTINATION})
SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${BIN_DESTINATION})
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${BIN_DESTINATION})
 
#include cmake files
include(${PROJECT_ROOT}/version.cmake)

find_package(Boost REQUIRED)                   #find boost
find_package(gflags REQUIRED COMPONENTS static)#find gflags
 
#set compile flags

set(CMAKE_CXX_FLAGS "-g3 -rdynamic -std=c++11")
set(CMAKE_CXX_FLAGS_DEBUG "-g3 -O0 -fsanitize=address -fno-omit-frame-pointer -fsanitize=leak")
set(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
 
#include dirs
include_directories(./ 
    $ENV{BOOST_ROOT})
 
#link dirs
link_directories(${BIN_DESTINATION})

 
#execute 
SET(SRC_MAIN demo.cpp main.cpp )
add_executable( ${PROJECT_NAME} ${SRC_MAIN})
set_target_properties(${PROJECT_NAME} PROPERTIES VERSION ${PROJECT_VERSION})    
target_link_libraries(${PROJECT_NAME} gflags ) #link gflags

创建并进入build目录,然后执行命令:cmake .. && make && ./main

编译前,需要正确安装boost并配置BOOST_ROOT环境变量;正确安装gflags(当然你也可以删除gflags相关的代码,并不影响boost::intrusive_ptr<>的demo)

发现并没有出现内存泄漏,创建的对象均正确释放了,并且没有任何的错误(双重删除问题);

猜你喜欢

转载自blog.csdn.net/lianshaohua/article/details/109185810