引用计数的实现
- reference_object.cc
总结
- 引用计数可以减少对象的构造、析构的次数,一般用在一些开销比较大的对象上,或者需要对资源进行管理的对象上(比如套接字描述符被文件描述符)
- 如果是POD类型或者简单类型,不要使用引用计数,反而会降低效率
- 如果是要求线程安全的,则直接使用c++11提供的智能指针即可,除非是特别要求性能且不要求线程安全则可以实现自己的引用计数
- 引用计数设计的时候,通过代理模式比侵入式的要好,使用方便,可以不用修改已有代码
- 引用计数的线程安全指的是其计数的线程安全,而不是其所持有的对象的线程安全
引用计数类的设计
引用计数类最简单的形式就是在需要的类中增加一个计数器,以带有引用计数的字符串类为例:
class RfString
{
public:
RfString(const char *s)
{
m_length = strlen(s);
m_buf = new char[m_length + 1];
memcpy(m_buf,s,m_length);
m_refer_counter = new size_t;
*m_refer_counter = 1;
}
RfString(const RfString &s):m_buf(s.m_buf),m_length(s.m_length)
{
m_refer_counter = s.m_refer_counter;
*m_refer_counter++;
}
RfString(RfString &&s)
{
std::swap(m_buf,s.m_buf);
std::swap(m_length,s.m_length);
std::swap(m_refer_counter,s.m_refer_counter);
}
RfString &operator=(const RfString &s)
{
if (&s == this)
{
return *this;
}
m_buf = s.m_buf;
m_length = s.m_length;
}
private:
private:
char *m_buf{nullptr};
size_t m_length{0};
size_t *m_refer_counter{nullptr}; // 引用计数
};
上面的代码虽然实现了字符串类的引用计数,但是却存在2个问题:
- 代码是侵入式的,如果要在已有的类上使用引用计数功能,则必须修改当前的类,违背了设计模式中的开闭原则
- 即使不考虑设计模式,效率上也有瑕疵,这里每次构造的时候都要拷贝3个成员变量,如果不是string类,而是一个含有成员较多的类呢?这些拷贝也会导致大量的构造函数的调用
- 引用计数是否线程安全不可选
引用计数类的模板实现
// refer_counter.h
#include <string>
#include <atomic>
#include <iostream>
#include <string.h>
template <typename T,typename CounterType = std::atomic<size_t>>
class ReferrenceObject
{
public:
ReferrenceObject(T *t):m_object(t)
{
m_refer_count = new CounterType;
*m_refer_count = 1;
}
~ReferrenceObject()
{
if (--(*m_refer_count) == 0)
{
delete m_object;
}
}
ReferrenceObject(const ReferrenceObject &r):m_object(r.m_object),m_refer_count(r.m_refer_count)
{
(*m_refer_count)++;
}
ReferrenceObject(ReferrenceObject &&r)
{
std::swap(m_object,r.m_object);
std::swap(m_refer_count,r.m_refer_count);
}
size_t refer_count() const
{
return *m_refer_count;
}
ReferrenceObject& operator=(const ReferrenceObject &r)
{
if (&r == this)
{
return *this;
}
if (--(*m_refer_count) == 0)
{
delete m_object;
}
m_object = r.m_object;
m_refer_count = r.m_refer_count;
(*m_refer_count)++;
return *this;
}
T &operator*()
{
return *m_object;
}
const T &operator*() const
{
return *m_object;
}
T *operator->()
{
return m_object;
}
const T *operator->() const
{
return m_object;
}
private:
T *m_object{nullptr};
CounterType *m_refer_count{nullptr};
};
引用计数类的使用
#include "refer_counter.h"
class String
{
public:
String(const char *s)
{
m_size = strlen(s);
m_buf = new char[m_size + 1];
memcpy(m_buf,s,m_size);
}
// 省略拷贝构造和拷贝赋值及优质拷贝构造
friend std::ostream &operator<<(std::ostream &os,const String &s)
{
os << s.m_buf;
return os;
}
~String()
{
delete m_buf;
}
private:
char *m_buf{nullptr};
size_t m_size{0};
};
using SafeRfString = ReferrenceObject<String,std::atomic<size_t>>;// 线程安全的引用计数
using RfString = ReferrenceObject<String,size_t>; // 非线程安全的引用计数
int main()
{
RfString s(new String("hello world"));
RfString a2(s);
RfString a3(a2);
RfString b1(new String("how are you?"));
a2 = b1;
std::cout << *s << ",refer counter = " << s.refer_count() << std::endl;
std::cout << *b1 << ",refer counter = " << b1.refer_count() << std::endl;
return 0;
}
// g++ -std=c++11 reference_object.cc
// output:
// hello world,refer counter = 2
// how are you?,refer counter = 2