C++个人使用经验手册

scope_ptr使用心得

    • 引言
    • boost::scoped_ptr使用小结
      • 禁用类的某个功能,可将该功能定义为私有成员 -- scoped_ptr源码启示
    • std::vector的reserve函数的坑
    • std::uniq_ptr
    • guard方式加锁
    • 类中封装函数指针的妙用 -- 底层服务框架初始化常用方法

引言

持续更新中…
个人经验记录笔记而已,仅供参考,下附各种官方学习网站:
boost库中文网:http://zh.highscore.de/cpp/boost/smartpointers.html
cpp reference中文网:https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5

boost::scoped_ptr使用小结

一个作用域指针独占一个动态分配的对象。 对应的类名为 boost::scoped_ptr,它的定义在 boost/scoped_ptr.hpp 中。 不像 std::auto_ptr,一个作用域指针不能传递它所包含的对象的所有权到另一个作用域指针。 一旦用一个地址来初始化,这个动态分配的对象将在析构阶段释放。
因为一个作用域指针只是简单保存和独占一个内存地址,所以 boost::scoped_ptr 的实现就要比 std::auto_ptr 简单。 在不需要所有权传递的时候应该优先使用 boost::scoped_ptr 。 在这些情况下,比起 std::auto_ptr 它是一个更好的选择,因为可以避免不经意间的所有权传递。

#include  

int main() 
{ 
  boost::scoped_ptr<int> i(new int); 
  *i = 1; 
  *i.get() = 2; 
  i.reset(new int); 
} 

使用reset时会为其初始化新指向的对象,原指向的对象会被释放。
我们不能对scoped_ptr进行任何赋值操作,无论是在=左侧还是右侧
通常,如果我们在一个类中用scoped_ptr定义了一个指针,那意味着每个新的对象都需要独立初始化一个该指针,如下:

class BClass {
public:
    BClass();
    virtual ~BClass();
    bool is_bclass();
private:
    int m_b;
};
class AClass {
public:
    AClass();
    AClass(int, BClass*);
    virtual ~AClass();
private:
    int m_a;
    boost::scoped_ptr<BClass> m_bclassor;
};
// 我们不能定义下面这种构造函数,即时定义了也用不了,因为不能将b_class的值赋值给一个scope_ptr变量
AClass::AClass(int a, BClass* b_class) : m_a(a), m_bclass(b_class) {};
// 只能在构造函数里初始化该指针
AClass::AClass() : m_bclass(new BClass()) {}

如果我们是希望能在A中存储另外一个已经初始化好的其他类指针,最好不要用。

使用的时候一般都是m_bclassor.get()->is_bclass()这种方式

禁用类的某个功能,可将该功能定义为私有成员 – scoped_ptr源码启示

像scoped_ptr就是将=运算符、比较运算符==、!=进行了重载,禁止了拷贝构造

template<class T> class scoped_ptr // noncopyable                                                                                                                                                                   
{
private:

    T * px;
  //拷贝构造函数,复制函数私有化,防止复制
    scoped_ptr(scoped_ptr const &);
    scoped_ptr & operator=(scoped_ptr const &);

    typedef scoped_ptr<T> this_type;
    //禁止同类对象进行一致性比较
    void operator==( scoped_ptr const& ) const;
    void operator!=( scoped_ptr const& ) const;

public:

    typedef T element_type;
    //显式构造函数
    explicit scoped_ptr( T * p = 0 ): px( p ) // never throws
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_constructor_hook( px );
#endif
    }

#ifndef BOOST_NO_AUTO_PTR

    explicit scoped_ptr( std::auto_ptr<T> p ) BOOST_NOEXCEPT : px( p.release() )
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_constructor_hook( px );
#endif
    }

#endif
    //析构函数
    ~scoped_ptr() // never throws
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_destructor_hook( px );
#endif
        boost::checked_delete( px );
    }
    //重置指针,
    void reset(T * p = 0) // never throws
    {
        BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors
        this_type(p).swap(*this);
    }
    //*操作符重载
    T & operator*() const // never throws
    {
        BOOST_ASSERT( px != 0 );
        return *px;
    }
    //->操作符重载
    T * operator->() const // never throws                                                                                                                                                                          
    {
        BOOST_ASSERT( px != 0 );
        return px;
    }
    //get返回对象指针,返回的指针不能作delete操作,不然会发生未定义行为
    T * get() const BOOST_NOEXCEPT
    {
        return px;
    }

// implicit conversion to "bool"
#include 
    //用于交换两个scoped_ptr保存的指针
    void swap(scoped_ptr & b) BOOST_NOEXCEPT
    {
        T * tmp = b.px;
        b.px = px;
        px = tmp;
    }
};

#if !defined( BOOST_NO_CXX11_NULLPTR )
//与空指针进行比较
template<class T> inline bool operator==( scoped_ptr<T> const & p, boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT
{
    return p.get() == 0;
}

template<class T> inline bool operator==( boost::detail::sp_nullptr_t, scoped_ptr<T> const & p ) BOOST_NOEXCEPT
{
    return p.get() == 0;
}

template<class T> inline bool operator!=( scoped_ptr<T> const & p, boost::detail::sp_nullptr_t ) BOOST_NOEXCEPT
{
    return p.get() != 0;
}

template<class T> inline bool operator!=( boost::detail::sp_nullptr_t, scoped_ptr<T> const & p ) BOOST_NOEXCEPT
{
    return p.get() != 0;
}

#endif
//交换函数
template<class T> inline void swap(scoped_ptr<T> & a, scoped_ptr<T> & b) BOOST_NOEXCEPT
{
    a.swap(b);
}
//get_pointer方法,类似get()方法。
template<class T> inline T * get_pointer(scoped_ptr<T> const & p) BOOST_NOEXCEPT
{
    return p.get();
}

std::vector的reserve函数的坑

使用reserve()函数,可以使vector预留指定数量的空间,使得添加元素时不会触发重新分配内存的操作,从而提高程序的效率。但同时需要注意reserve函数不会初始化数组,如果直接使用会出core。

std::uniq_ptr

std::unique_ptr 是通过指针占有并管理另一对象,并在 unique_ptr 离开作用域时释放该对象的智能指针。
在下列两者之一发生时用关联的删除器释放对象:

  • 销毁了管理的 unique_ptr 对象
  • 通过 operator= 或 reset() 赋值另一指针给管理的 unique_ptr 对象。

实践重点:
与上面的scoped_ptr不同的是,它可以通过:

A* ptr1 = uniq_ptr;

这种方式,将原uniq_ptr的管理权交给ptr1,但与次同时,原uniq_ptr不在拥有对原对象的管理所有权,如果再次使用uniq_ptr操作对象会出现运行时错误,对大型服务来说是很致命的,这是需要特别注意的。至于通过新构造一个对象reset进去,与scoped_ptr类似。
其他三个关注点:
(1)unique_ptr 亦可以不占有对象,该情况下称它为空 (empty);
(2)可移动构造或移动赋值,不可拷贝构造或拷贝赋值;
(3)只有非 const 的 unique_ptr 能转移被管理对象的所有权给另一 unique_ptr 。若对象的生存期为 const std::unique_ptr 所管理,则它被限定在创建指针的作用域中。

guard方式加锁

所谓guard方式加锁,就是让锁出作用于后自动销毁,这种写法有能避免遗漏解锁而导致的死锁,有人可能会想我怎么可能会忘解锁,这种场景很常见,比如:

std::map<std::string, T data> G_data_map;
bool get_my_data(const std::string& key, 
                 T& my_data) {
    pthread_rwlock_rdlock(&_intervent_info_rwlock);
    auto it = G_data_map.find(key);
    if (it == G_data_map.end()) {
        return false;
    }
    my_data = it->second;
    pthread_rwlock_unlock(&_intervent_info_rwlock);
    
    return true;
}

上面这段代码在返回的时候就没有解锁就返回了。这种是在开发中很容易犯的问题。特别是加锁的代码范围比较大的时候。
如果使用linux系统库的锁api,可以自己封装一个锁的类,在析构函数里面释放锁即可,这样就实现了简单实用的guard方式。

类中封装函数指针的妙用 – 底层服务框架初始化常用方法

近期在工作中看到个比较巧妙的写法,在类里面封装一个public的函数指针:
(1)可以让该类方便的地调用类外部的函数的同时,外部函数也能较方便地通过内部成员函数调用该函数来使用类内部数据;
(2)可以方便地在外部重写新的函数,赋值给该函数指针;这在写底层框架时很有用,我们有时就是想把一个接口的实现放到上层去做,底层框架只管调用该函数执行即可;
话不多说,上代码更容易理解:

typedef void (* service_init_callback) (ServerContext& context, const Configure& appConf);
// 先在本文件定义一个servers_init函数,my_server里面的接口对其调用等价于调用my_server内部的回调函数,这样定义是为了可读性,避免直接调用回调函数影响可读性
void service_init(MyServer& my_server, Configure& appConf) {
    Context& context = my_server.get_context();
    if(my_server.service_func != NULL){
         my_server.service_func(context, appConf);
    }
} 
class MyServer {
public:
    MyServer(char* module_name);
    ~MyServer();

    ServerContext& get_context();
    int run(int argc, char* argv[]);

public:
    // 定义一个回调函数
    service_init_callback service_func;

private:
    int init(int argc, char* argv[]) {
        Configure appconf = context.get_configure(); // 一般configure信息也存在context中
        server_init(context, appconf);
    }
private:
    UiiContext context;
};
};

这样我们可以方便地用service_func访问类中的context,虽然我们一般都可以通过service_func来将context进行传递。

void server_init(ServerContext& context, const Configure& appConf) {
    // ... 
}
MyServer my_server;
myserver.service_func = server_init;

这样就实现了将函数指针传递给一个服务初始化函数,然后在适当的时候调用它。并且比较好地封装了context,我们不用先在外面显示地初始化context然后和回调函数一块作为参数传入。

你可能感兴趣的:(c++)