#include<iostream>
using namespace std;
 
class String;
ostream& operator<<(ostream &out, const String&s);
//引用计数器类
class String_rep 
{
    friend class String;
    friend ostream& operator<<(ostream &out, const String&s);
public:
        String_rep(const char *str )
            :use_count(0)
        {
            if (str == NULL)
            {
                data = new char[1];
                data[0] = '\0';
            }
            else
            {
                data = new char[strlen(str) + 1];
                strcpy(data, str);
            }
        }
    
        String_rep(const String_rep  &rep) :use_count(0)
        {
            data = new  char[strlen(rep.data) + 1];
            strcpy(data, rep.data);
        }
        String_rep&  operator=(const String_rep &rep)
        {
            if (this != &rep)
            {
                delete[]data;
                data = new char[strlen(rep.data) + 1];
                strcpy(data, rep.data);
            }
            return *this;
        }
        ~String_rep()
        {
                delete[]data;
                data = NULL;
        }
public:
    void increase()
    {
        ++use_count;
    }
    
    void decrease()
    {
        if (use_count == 0)
        {
            delete this; //自杀行为    释放this所指的空间,在释放之前调动这个类的析构函数
        }
    }
private:
        char *data;
        int use_count;
};
////////////////////////////////////////////////////////////////////////////////////////
class String
{
     friend ostream& operator<<(ostream &out, const String&s);
public:
    String(const char* str = " ")
    {
        rep = new String_rep(str);
        rep->increase();
    }
    String(const String &s)
    {
        rep = s.rep;      //浅拷贝
        rep->increase();
    }
    String& operator=(const String &s)
    {
        if (this != &s)
        {
            rep->decrease();   //模拟delete
            rep = s.rep;           //模拟new
            rep->increase();      //模拟strcpy
            /*rep = s.rep;   //这会更改引用计数器指针 ,造成s内存泄漏
            rep->increase();*/
        }
        return *this;
    }
        ~String()
        {
            rep->decrease();
        }
public:
    void to_upper()
    {
        if (rep->use_count > 1)
        {
            String_rep*  new_rep = new String_rep(rep->data);
            rep->decrease();
            rep = new_rep;
            rep->increase();
        }
        char* ch = rep->data;
        while (*ch != '\0')
        {
            *ch -= 32;
            ++ch;
        }
    }
private:
    String_rep *rep;  //引用计数器
};
ostream& operator<<(ostream &out, const String&s)
{
    out << s.rep->data;
    return out;
}
void main()
{
    String s1("hello");
    String s2(s1);
    String s3;
    s3 = s2;
    cout << "s1=" << s1 << endl;
    cout << "s2=" << s2 << endl;
    cout << "s3=" << s3 << endl;

s2.to_upper();

cout << "-----------------------------------------------" << endl;
   
    cout << "s1=" << s1 << endl;
    cout << "s2=" << s2 << endl;
    cout << "s3=" << s3 << endl;
}

String类的写时拷贝的更多相关文章

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

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

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

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

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

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

  4. String写时拷贝实现

    头文件部分 1 /* 版权信息:狼 文件名称:String.h 文件标识: 摘 要:对于上版本简易的String进行优化跟进. 改进 1.(将小块内存问题与大块分别对待)小内存块每个对象都有,当内存需 ...

  5. 计算机程序的思维逻辑 (73) - 并发容器 - 写时拷贝的List和Set

    本节以及接下来的几节,我们探讨Java并发包中的容器类.本节先介绍两个简单的类CopyOnWriteArrayList和CopyOnWriteArraySet,讨论它们的用法和实现原理.它们的用法比较 ...

  6. 并发容器之写时拷贝的 List 和 Set

    对于一个对象来说,我们为了保证它的并发性,通常会选择使用声明式加锁方式交由我们的 Java 虚拟机来完成自动的加锁和释放锁的操作,例如我们的 synchronized.也会选择使用显式锁机制来主动的控 ...

  7. Java编程的逻辑 (73) - 并发容器 - 写时拷贝的List和Set

    ​本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http: ...

  8. 写时拷贝(Copy On Write)方案详解

    本文旨在通过对 写时拷贝 的四个方案(Copy On Write)分析,让大家明白写时拷贝的实现及原理. 关于浅拷贝与深拷贝,我在之前的博客中已经阐述过了  浅拷贝容易出现指针悬挂的问题,深拷贝效率低 ...

  9. rust漫游 - 写时拷贝 Cow<'_, B>

    rust漫游 - 写时拷贝 Cow<'_, B> Cow 是一个写时复制功能的智能指针,在数据需要修改或者所有权发生变化时使用,多用于读多写少的场景. pub enum Cow<'a ...

随机推荐

  1. Java编程思想学习笔记_6(并发)

    一.从任务中产生返回值,Callable接口的使用 Callable是一种具有泛型类型参数的泛型,它的类型参数表示的是从方法call返回的值,而且必须使Executor.submit来去调用它.sub ...

  2. iOS开发之 Xcode svn更新代码后,不能打开.xcodeproj,因为该项目文件不能被解析

    http://www.cfanz.cn/?c=article&a=read&id=41565 解决方法:    1.对.xcodeproj 文件右键,显示包内容 2.双击打开 proj ...

  3. C++——将成员函数作为参数

    在C++中,成员函数指针作为参数传递给其他函数和普通函数指针的传递是不同的,首先 我们来回顾一下普通函数指针的传递方法: //------------------------------------- ...

  4. jsp实时显示后台批处理进度 - out分块,简单的长连接方式

    这两天在实现一个批处理操作,但是想让前台实时显示后台批处理进度,本想着用复杂一些的框架可以实现异步信息调用 但是鉴于是内部管理系统,且只有一两个人用到这个功能,所以做了一个简单的长连接方式的实时响应 ...

  5. /dev/sda3: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY

    系统强制断电后,出现以下错误: /dev/sda3: UNEXPECTED INCONSISTENCY; RUN fsck MANUALLY 启动系统后在字符界面有两个选项,输入root密码进入维护模 ...

  6. Js_字符串操作

    字符串操作方法 charAt()以单字符串的形式返回给定位置的哪个字符串 例: var num = "hello world"; alert(num.charAt(1))//“e” ...

  7. C# Socket编程(2)识别网络主机

    通过前面的笔记我们可以知道:一个客户端要想发起一次通信,先决条件就是需要知道运行在服务端程序的主机的IP地址是多少,端口号是多少.然后我们才能够通过这个地址向服务器特定的应用程序发送信息.对于网络上的 ...

  8. python语法笔记(三)

    1. 动态类型 python的变量不需要声明,在赋值时,变量可以赋值为任意的值.这和Python的动态类型语言相关. python对象是存在于内存中的实体,代码中写对象名,只是指向该对象的引用.引用和 ...

  9. 【转载】PHP运行模式的深入理解

    PHP运行模式的深入理解 作者: 字体:[增加 减小] 类型:转载 时间:2013-06-03我要评论 本篇文章是对PHP运行模式进行了详细的分析介绍,需要的朋友参考下   PHP运行模式有4钟:1) ...

  10. kubernetes容器编排系统介绍

    版权声明:本文由turboxu原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/152 来源:腾云阁 https://www. ...