碰到过一位一直怀疑C++标准库(STL)效率的人,他说STL效率太低,企业开发根本不会用。我是持反对意见的。

说这话的人,肯定没有做过大量的调查。没有调查就没有发言权。

STL的效率是不低的,足够满足现在的绝大部分需求了。特别是当前的操作系统和硬件都以页为内存的基本管理单位,并且32位的系统(嵌入式还挺多的,但是嵌入式对内存的需求很大的比较少吧)的已经不是很多了。内存碎片的问题也就并不明显了。

前面说的与这里要说的是无关的,这里指向说一说String封装中的读共享,写复制。

学习过linux/unix系统编程的人,应该对读共享,写复制这个概念有一个比较清晰的了解,这个可见APUE的进程相关的章节。

实现原理

这个实现原理其实很简单,如果学习了shared_ptr智能指针,那应该是可以猜得到的。

其实关键的地方就是引用计数了。如果在string对象拷贝构造或者赋值(用已有对象)的时候,不进行拷贝,而只是进行引用计数的增加,数据采用共享方式。而在需要进行写操作的时候,才进行真正的拷贝操作。

代码实现

这里只是一个简单的实现,来说明这个原理,并没有多少实用价值。并且没有做到多线程安全。现在一般的也不会采取这种做法,因为现在内存都比较富裕了,还要解决多线程安全问题。VC6还是采用的COW技术,现在编译器自带的STL基本都不在采用,而改用(忘记名字了,原理就是内部使用一个数组,只有创建的字符串长度超过这个数组的时候,才进行内存分配)。

#include <stdio.h>
#include <string.h> struct shared_ptr{
char* data; //数据
int ref; //引用计数
}; class String{
public:
String(const char* str=NULL):iswrite(false)
{
p = new shared_ptr;
if( str != NULL){
p->data = new char[strlen(str)+1];
strcpy(p->data,str);
}
else{
p->data = new char[1];
p->data[0]='\0';
}
p->ref = 1;
}
String(const String& s):iswrite(false)
{
p = s.p;
p->ref += 1;
}
~String()
{
if(p->ref == 1){
delete p->data;
delete p;
}
else{
p->ref -=1;
}
} String& erase(int first,int last)
{
if(first < 0 ||last > strlen(p->data))return *this; if(!iswrite){ //如果不是可写状态
shared_ptr* t=p;
p = new shared_ptr; //拷贝数据
p->data = new char[strlen(t->data)+1];
strcpy(p->data,t->data);
p->ref = 1;
t->ref -=1; //原指向结构体引用计数减一
}
//擦除操作
int len = strlen(p->data);
for(int i = 0;i<len-last+1;++i){
p->data[first + i] = p->data[last + i];
}
return *this;
}
void show() const
{
printf("ref = %d,data:%s\n",p->ref,p->data);
}
private:
shared_ptr* p; //数据
bool iswrite;//可写?
}; int main()
{ String s1("hello world");
String s2(s1);
String s3(s2);
s1.show();
s2.show();
s3.show(); s2.erase(5,10);
s1.show();
s2.show();
s3.show();
return 0;
}

运行结果

ref = 3,data:hello world
ref = 3,data:hello world
ref = 3,data:hello world
ref = 2,data:hello world
ref = 1,data:hellod
ref = 2,data:hello world

String封装——读时共享,写时复制的更多相关文章

  1. php变量 写时改变 写时复制

    写时复制 $var = 1; $var2 = $var; #此时$var2 与 $var 指向同一个zval refcount = 2: $var = 2; # 此时$val 改变 所以 $var 与 ...

  2. 线程不安全(Arraylist,HashSet,HashMap)和写时复制

    package com.yangyuanyuan.juc1205; import java.util.List; import java.util.Map; import java.util.Set; ...

  3. Elasticsearch 读时分词、写时分词

    初次接触 Elasticsearch 的同学经常会遇到分词相关的难题,比如如下这些场景: 为什么明明有包含搜索关键词的文档,但结果里面就没有相关文档呢?我存进去的文档到底被分成哪些词(term)了?我 ...

  4. 标准C++类std::string的内存共享和Copy-On-Write(写时拷贝)

    标准C++类std::string的内存共享,值得体会: 详见大牛:https://www.douban.com/group/topic/19621165/ 顾名思义,内存共享,就是两个乃至更多的对象 ...

  5. c++ string写时复制

    string写时复制:将字符串str1赋值给str2后,除非str1的内容已经被改变,否则str2和str1共享内存.当str1被修改之后,stl才为str2开辟内存空间,并初始化. #include ...

  6. 写时复制和fork,vfork,clone

    写时复制 原理: 用了“引用计数”,会有一个变量用于保存引用的数量.当第一个类构造时,string的构造函数会根据传入的参数从堆上分配内存,当有其它类需要这块内存时,这个计数为自动累加,当有类析构时, ...

  7. String类的实现(4)写时拷贝浅析

    由于释放内存空间,开辟内存空间时花费时间,因此,在我们在不需要写,只是读的时候就可以不用新开辟内存空间,就用浅拷贝的方式创建对象,当我们需要写的时候才去新开辟内存空间.这种方法就是写时拷贝.这也是一种 ...

  8. String 类的实现(2)引用计数与写时拷贝

    1.引用计数 我们知道在C++中动态开辟空间时是用字符new和delete的.其中使用new test[N]方式开辟空间时实际上是开辟了(N*sizeof(test)+4)字节的空间.如图示其中保存N ...

  9. fork()和写时复制

    写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当发出fork(  )系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程.这种行为是非常耗时的,因为它需要: · ...

随机推荐

  1. bs4_3select()

        Finding an Element with the select() Method 调用select()方法从BeautifulSoup对象索取网页元素,并用CSS 选择器传递你寻找的元素 ...

  2. C#中使用DateTimePicker控件显示修改日期时间

    1.只显示日期   默认就是   2.只显示时间   修改属性 Format 设为Time ShowUpDown设为true   3.同时显示日期时间   Format设为Custom CustomF ...

  3. 比较详细Python正则表达式操作指南(re使用)

    比较详细Python正则表达式操作指南(re使用) Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式.Python 1.5之前版本则是通过 regex 模块提供 E ...

  4. OpenGL瓶颈

    在优化Erya3D引擎的过程中,遇到的瓶颈: 1. 字符串操作,避免逐个字符的比较,使用哈希码比较2. 贴图操作:切换绑定贴图.更改贴图参数3. 切换绑定GLSL程序4. Draw Call:http ...

  5. CMake安装(linux)

    环境:CentOS6.5 1.下载网址: http://www.cmake.org/download/ (选择linux版本,编译安装) 2.安装 # tar zxvf cmake-.tar.gz # ...

  6. nginx和fpm的进程数配置和502,504错误

    502 和 php-fpm.conf 1.php-cgi进程数不够用.php执行时间长,导致没有空闲进程处理新请求. 2.php-cgi进程死掉.php-fpm超时时间短,当前进程执行超时关闭连接. ...

  7. Spring系列之beanFactory与ApplicationContext

    一.BeanFactoryBeanFactory 是 Spring 的“心脏”.它就是 Spring IoC 容器的真面目.Spring 使用 BeanFactory 来实例化.配置和管理 Bean. ...

  8. margin小结

    一. margin百分比 1. 普通元素的百分比margin都是相对于容器的宽度计算 2. 绝对定位元素的百分比margin是相对于第一个定位祖先元素(relative/absolute/fixed) ...

  9. 安装windows后重新修复grub2的引导

    前段时间,我的用的双系统windows8.1 + fedora 21 workstation,使用grub2引导fedora和windows8.1的启动.由于一些原因,导致我的windows8.1无法 ...

  10. jupyter notebook + pyspark 环境搭建

    安装并启动jupyter 安装 Anaconda 后, 再安装 jupyter pip install jupyter 设置环境 ipython --ipython-dir= # override t ...