zoukankan      html  css  js  c++  java
  • C++ 之 stl::string 写时拷贝导致的问题

    一、写时拷贝原理

    String是使用计数器来记录引用计数,当有新的string对象共享内存块时,计数器+1,当有对象触发写时拷贝或析构时,计数器-1。

    那么计数器存放在哪里呢?最合适的就是在堆里分配空间专门存储这个计数器,由第一个创建的对象分配并初始化计数器,其他对象按照约定引用计数器。我们知道string的内存空间就在堆上,直接在这块区上多分配一个空间来存储计数器是最方便的,所有共享这块内存的string对象都能访问计数器。事实上STL就是这么实现的,在string内存空间的最前面分配了空间存储计数器.

     
    图片描述

    string的所有赋值、拷贝构造操作,计数器都会+1;修改string数据时,先判断计数器是否为0(0代表没有其他对象共享内存空间),为0则可以直接使用内存空间,否则触发写时拷贝,计数器-1,拷贝一份数据出来修改,并且新的内存计数器置0;string对象析构时,如果计数器为0则释放内存空间,否则计数器也要-1。

    二、写时拷贝容易引发的问题

    问题主要出现在直接使用 sprintf 操作 string 的内部指针地址 (char)s.c_str() 。 

    #include <stdio.h>
    #include <iostream>
    #include <string>

    int main()
    {
    std::string s1 = "efghijk";
    std::string s2 = s1;
    std::string s3 = s1;
    sprintf((char*)s1.c_str(), "%s", "abcde");

    std::cout << "s1=" << s1 << std::endl;
    std::cout << "s2=" << s2 << std::endl;
    std::cout << "s3=" << s3 << std::endl;
    }

    输出结果为(j没有显示在string是由于在sprintf时,abcde后面还有一个字符):
    s1=abcdek
    s2=abcdek
    s3=abcdek

    通过 string::resize() 分配内存空间。 通过 string::c_str() 直接获取内存空间的起始地址并写入数据。

    这样会导致s1和s2和s3都发生变化,所以尽量不要使用string的.c_str函数进行赋值操作,不然容易由于copy on right导致的无法直观想到的错误。

  • 相关阅读:
    JavaSE知识-14(正则表达式&常用工具类)
    JavaSE知识-13(StringBuffer&数组排序)
    JavaSE知识-12(String类)
    JavaSE知识-11(Eclipse使用以及Object类型)
    JavaSE知识-10(面向对象_权限修饰符&匿名内部类)
    JavaSE知识-09(面向对象_多态&抽象类&接口)
    JavaSE知识-08(面向对象_继承&方法&final)
    JavaSE知识-07(面向对象-构造方法&静态static)
    20145205 20145231 《信息安全系统设计基础》第二次实验
    20145231 《信息安全系统设计基础》第9周学习总结
  • 原文地址:https://www.cnblogs.com/daimadebanyungong/p/12204702.html
Copyright © 2011-2022 走看看