copy on write
B从A处复制了一个复杂对象,B当且仅当要写这个对象的时候才从A拷贝一份真实的数据过来。写时才拷贝(Copy-On-Write)技术,就是编程界“懒惰行为”——拖延战术的产物。举个例子,比如我们有个程序要写文件,不断地根据网络传来的数据写,如果每一次fwrite或是fprintf都要进行一个磁盘的I/O操作的话,都简直就是性能上巨大的损失,因此通常的做法是,每次写文件操作都写在特定大小的一块内存中(磁盘缓存),只有当我们关闭文件时,才写到磁盘上(这就是为什么如果文件不关闭,所写的东西会丢失的原因)。
#include <iostream>
#include <cstring>
#include <chrono>
#include <vector>
#include <sstream>
#include <iterator>
#include <algorithm>
// COW string
struct String {
private:
struct Impl {
size_t length;
size_t capacity;
size_t use_count;
Impl(size_t length, size_t capacity, size_t use_count)
:length(length),
capacity(capacity),
use_count(use_count) {
}
Impl(const Impl &other)
:length(other.length),
capacity(other.capacity),
use_count(other.use_count) {
}
};
char* ptr;
Impl* impl;
public:
explicit String(const char* str);
String(const String &other);
~String();
struct WriteProxy {
void operator= (char c) {
if (ptr[index] != c && impl->use_count != 1) {
char *new_data = new char[impl->capacity];
strncpy(new_data, ptr, impl->length);
ptr = new_data;
Impl *new_impl = new Impl(*impl);
--impl->use_count;
new_impl->use_count = 1;
impl = new_impl;
}
ptr[index] = c;
}
~WriteProxy() {
std::clog << "delete WriteProxy" << std::endl;
}
char* &ptr;
Impl* &impl;
size_t index;
};
WriteProxy operator[](size_t index) {
std::clog << "WriteProxy" << std::endl;
return WriteProxy {ptr, impl, index};
}
char operator[](size_t index) const {
std::clog << "char operator[]" << std::endl;
return ptr[index];
}
friend std::ostream &operator<<(std::ostream &os, const String &str) {
os << str.ptr << std::endl;
return os;
}
};
String::String(const char *str) {
ptr = new char[strlen(str) + 1];
strcpy(ptr, str);
impl = new Impl {strlen(str), strlen(str) + 1, 1};
}
String::String(const String &other) {
ptr = other.ptr;
impl = other.impl;
++impl->use_count;
}
String::~String() {
--impl->use_count;
if (impl->use_count == 0) {
delete[] ptr;
delete impl;
}
}
int main() {
String a("Hello");
std::clog << a;
String b(a);
std::clog << b;
String c(a);
std::clog << c;
auto jj = c[3];
jj = '3';
std::clog << a;
std::clog << b;
std::clog << c;
return 0;
}
核心代码
struct WriteProxy {
void operator= (char c) {
if (ptr[index] != c && impl->use_count != 1) {
char *new_data = new char[impl->capacity];
strncpy(new_data, ptr, impl->length);
ptr = new_data;
Impl *new_impl = new Impl(*impl);
--impl->use_count;
new_impl->use_count = 1;
impl = new_impl;
}
ptr[index] = c;
}
char* &ptr;
Impl* &impl;
size_t index;
};
WriteProxy operator[](size_t index) {
return WriteProxy {ptr, impl, index};
}
将 String 类的 ptr 和 impl 传给了 char &ptr; Impl &impl; 所以在 WriteProxy 中是可以直接访问到 String 类中的 ptr 和 impl 。