copy on write

B从A处复制了一个复杂对象,B当且仅当要写这个对象的时候才从A拷贝一份真实的数据过来。写时才拷贝(Copy-On-Write)技术,就是编程界“懒惰行为”——拖延战术的产物。举个例子,比如我们有个程序要写文件,不断地根据网络传来的数据写,如果每一次fwrite或是fprintf都要进行一个磁盘的I/O操作的话,都简直就是性能上巨大的损失,因此通常的做法是,每次写文件操作都写在特定大小的一块内存中(磁盘缓存),只有当我们关闭文件时,才写到磁盘上(这就是为什么如果文件不关闭,所写的东西会丢失的原因)。

  1. #include <iostream>
  2. #include <cstring>
  3. #include <chrono>
  4. #include <vector>
  5. #include <sstream>
  6. #include <iterator>
  7. #include <algorithm>
  8. // COW string
  9. struct String {
  10. private:
  11. struct Impl {
  12. size_t length;
  13. size_t capacity;
  14. size_t use_count;
  15. Impl(size_t length, size_t capacity, size_t use_count)
  16. :length(length),
  17. capacity(capacity),
  18. use_count(use_count) {
  19. }
  20. Impl(const Impl &other)
  21. :length(other.length),
  22. capacity(other.capacity),
  23. use_count(other.use_count) {
  24. }
  25. };
  26. char* ptr;
  27. Impl* impl;
  28. public:
  29. explicit String(const char* str);
  30. String(const String &other);
  31. ~String();
  32. struct WriteProxy {
  33. void operator= (char c) {
  34. if (ptr[index] != c && impl->use_count != 1) {
  35. char *new_data = new char[impl->capacity];
  36. strncpy(new_data, ptr, impl->length);
  37. ptr = new_data;
  38. Impl *new_impl = new Impl(*impl);
  39. --impl->use_count;
  40. new_impl->use_count = 1;
  41. impl = new_impl;
  42. }
  43. ptr[index] = c;
  44. }
  45. ~WriteProxy() {
  46. std::clog << "delete WriteProxy" << std::endl;
  47. }
  48. char* &ptr;
  49. Impl* &impl;
  50. size_t index;
  51. };
  52. WriteProxy operator[](size_t index) {
  53. std::clog << "WriteProxy" << std::endl;
  54. return WriteProxy {ptr, impl, index};
  55. }
  56. char operator[](size_t index) const {
  57. std::clog << "char operator[]" << std::endl;
  58. return ptr[index];
  59. }
  60. friend std::ostream &operator<<(std::ostream &os, const String &str) {
  61. os << str.ptr << std::endl;
  62. return os;
  63. }
  64. };
  65. String::String(const char *str) {
  66. ptr = new char[strlen(str) + 1];
  67. strcpy(ptr, str);
  68. impl = new Impl {strlen(str), strlen(str) + 1, 1};
  69. }
  70. String::String(const String &other) {
  71. ptr = other.ptr;
  72. impl = other.impl;
  73. ++impl->use_count;
  74. }
  75. String::~String() {
  76. --impl->use_count;
  77. if (impl->use_count == 0) {
  78. delete[] ptr;
  79. delete impl;
  80. }
  81. }
  82. int main() {
  83. String a("Hello");
  84. std::clog << a;
  85. String b(a);
  86. std::clog << b;
  87. String c(a);
  88. std::clog << c;
  89. auto jj = c[3];
  90. jj = '3';
  91. std::clog << a;
  92. std::clog << b;
  93. std::clog << c;
  94. return 0;
  95. }

COW - 图1

核心代码

  1. struct WriteProxy {
  2. void operator= (char c) {
  3. if (ptr[index] != c && impl->use_count != 1) {
  4. char *new_data = new char[impl->capacity];
  5. strncpy(new_data, ptr, impl->length);
  6. ptr = new_data;
  7. Impl *new_impl = new Impl(*impl);
  8. --impl->use_count;
  9. new_impl->use_count = 1;
  10. impl = new_impl;
  11. }
  12. ptr[index] = c;
  13. }
  14. char* &ptr;
  15. Impl* &impl;
  16. size_t index;
  17. };
  18. WriteProxy operator[](size_t index) {
  19. return WriteProxy {ptr, impl, index};
  20. }

将 String 类的 ptr 和 impl 传给了 char &ptr; Impl &impl; 所以在 WriteProxy 中是可以直接访问到 String 类中的 ptr 和 impl 。