当前位置: 首页 > news >正文

人才网站建设报告本地wordpress站点上传

人才网站建设报告,本地wordpress站点上传,网站建设税收分类编码,请人做软件开发的网站深浅拷贝——利用模拟实现basic_string深入理解 一、深浅拷贝的基本概念 深拷贝和浅拷贝都是指在对象复制时#xff0c;复制对象的内存空间的方式。 1.1 深浅拷贝的不同之处 浅拷贝是指将一个对象的所有成员变量都直接拷贝给另一个对象#xff0c;包括指针成员变量#…深浅拷贝——利用模拟实现basic_string深入理解 一、深浅拷贝的基本概念 深拷贝和浅拷贝都是指在对象复制时复制对象的内存空间的方式。 1.1 深浅拷贝的不同之处 浅拷贝是指将一个对象的所有成员变量都直接拷贝给另一个对象包括指针成员变量两个对象共享同一块内存空间。 深拷贝是指在对象复制时分配一块新的内存空间将原对象的成员变量复制到新的内存空间中。 1.2 适用场景及优缺点 浅拷贝通常用于类的复制操作可以在一定程度上提高性能但是如果一个对象的成员变量指向的是堆内存空间那么当其中一个对象释放了这块内存空间后另一个对象仍然指向这块内存空间容易出现问题。 浅拷贝适用于对象的成员变量是基本数据类型或指向栈空间的指针的情况。因为这些数据类型的内存是在栈上分配的所以可以直接拷贝共享同一块内存空间也不会带来问题。 深拷贝可以避免两个对象共享同一块内存空间带来的问题但是也会增加内存空间的消耗以及复制成员变量的时间消耗。 深拷贝适用于对象的成员变量是指向堆内存空间的指针或者成员变量是对象的情况。因为这些情况下如果采用浅拷贝的方式两个对象共享同一块内存空间可能会导致一个对象释放了内存空间另一个对象还在使用这块内存空间造成不可预料的错误。 二、basic_string的模拟实现 2.1 中规中矩的传统写法 #includeiostream; #includestring.h;using namespace std;//深浅拷贝——利用模拟实现basic_string深入理解 namespace yfy {class string{public:string(const char* str):_str(new char[strlen(str) 1]) //若此处不在堆上开空间而是将str作为参数赋值给_str就是浅拷贝其后果是无法修改常量字符串{strcpy(_str, str);}string(const string s):_str(new char[strlen(s._str) 1]) //自定义拷贝构造函数深拷贝————开辟同样大小空间并将数据拷贝{strcpy(_str, s._str);}~string(){delete[] _str;_str nullptr;}char operator[](size_t pos){return _str[pos];}string operator(const string s) //不管左值右值谁长直接将左值释放开辟和右值同样大小空间再拷贝右值{if (this ! s) //防止自己给自己赋值{char* tmp new char[strlen(s._str) 1]; //如果先释放_strnew失败了会导致赔了夫人又折兵new如果失败会抛异常所以先不要释放_strnew成功后再释放delete[] _str; _str tmp;strcpy(_str, s._str);}return *this;}private:char* _str;};void test_string1(){string s1(hello);s1[0] x;string s2(s1);//若是编译器默认实现的拷贝构造则为浅拷贝————导致1、同一块空间析构两次 2、一个对象修改值另一个随之改变string s3(hello world);s1 s3;} }int main() {yfy::test_string1();return 0; }2.2 富有资本主义的现代写法 //拷贝构造string(const string s):_str(nullptr) //若不置空tmp作为临时变量在生命结束时调用析构此时tmp为一个随机值会出问题{string tmp(s._str); //调用构造函数为tmp开空间swap(_str, tmp._str);}//赋值string operator(string s){swap(_str, s._str);return *this;}3.3 basic_string模拟汇总 #includeiostream #includestring.h #includeassert.husing namespace std;//深浅拷贝——利用模拟实现basic_string深入理解 namespace yfy {class string{public:typedef char* iterator; //string 中的迭代器就是原生指针typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str _size;}const_iterator begin() const{return _str;}const_iterator end() const{return _str _size;}/*string() //默认构造函数:_str(new char[1]), _size(0), _capacity(0){*_str \0;//初始化}*/string(const char* str ) //给一个空串作为缺省值即可替代上面的默认构造函数:_size(strlen(str)),_capacity(_size){_str new char[_capacity 1];strcpy(_str, str);}void swap(string s){::swap(_str, s._str); //swap前加作用域限定符代表调用全局域中的swap函数若不加编译器无法区分::swap(_size, s._size);::swap(_capacity, s._capacity);}string(const string s):_str(nullptr) //若不置空tmp作为临时变量在生命结束时调用析构此时tmp为一个随机值会出问题{string tmp(s._str); //调用构造函数为tmp开空间this-swap(tmp);}~string(){delete[] _str;_str nullptr;}//可读可写char operator[](size_t pos){assert(pos _size);return _str[pos];}//只读const char operator[](size_t pos) const{assert(pos _size);return _str[pos];}string operator(string s){this-swap(s);return *this;}void reserve(size_t n) //重新开辟空间{if (n _capacity){char* tmp new char[n 1];strcpy(tmp, _str);delete[] _str;_str tmp;_capacity n;}}void resize(size_t n, char ch \0){if (n _size){_size n;_str[_size] \0;}else{if (n _capacity){reserve(n);}for (size_t i _size; i n; i){_str[i] ch;}_size n;_str[_size] \0;}}void push_back(char ch){//if (_size _capacity)//{// size_t newcapactiy _capacity 0 ? 4 : _capacity * 2;// reserve(newcapactiy);//}//_str[_size] ch;//_size;//_str[_size] \0;insert(_size, ch);}void append(const char* str){//size_t len strlen(str);//if (_size len _capacity)//{// reserve(_size len);//}//strcpy(_str _size, str);//_size len;insert(_size, str);}string operator(char ch){push_back(ch);return *this;}string operator(const char* str){append(str);return *this;}string operator(const string s){*this s._str;return *this;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}string insert(size_t pos, char ch){assert(pos _size);if (_size _capacity){size_t newcapacity _capacity 0 ? 4 : _capacity * 2;reserve(newcapacity);}int end _size; //进行头插时此处的end会变为负数在与pos比较时会整型提升到无符号数从而导致死循环while (end (int)pos) //解决方案将比较时的pos强转为int或控制end不能为负数{_str[end 1] _str[end];end--;}_str[pos] ch;_size;return *this;}string insert(size_t pos, const char* str){assert(pos _size);size_t len strlen(str);if (len 0) //插入空串{return *this;}if (_size len _capacity){reserve(_size len);}size_t end _size len;while (end pos len) //字符移位{_str[end] _str[end - len];end--;} for (int i 0; i len; i) //字符填充{_str[pos i] str[i];}_size len;return* this;}string erase(size_t pos, size_t len npos){assert(pos _size);if (len npos || pos len _size) //删除的长度大于剩余的长度{_str[pos] \0;_size pos;}else //删除的长度小于剩余长度需要把遗留字符移花接木 {strcpy(_str pos, _str pos len);_size - len;}return *this;}const char* c_str(){return _str;}void clear(){_str[0] \0;_size 0;}size_t find(char ch, size_t pos 0){for (size_t i pos; i _size; i){if (_str[i] ch){return i;}}return npos;}size_t find(const char* sub, size_t pos 0){const char* p strstr(_str pos, sub);if (p nullptr){return npos;}else{return p - _str;}}private:char* _str;size_t _size;size_t _capacity;static const size_t npos;};const size_t string::npos -1;ostream operator(ostream out, const string s){for (size_t i 0; i s.size(); i){out s[i];}return out;}istream operator(istream in, string s){s.clear();char ch;//in ch; //如此写会自动忽略换行和空格导致死循环ch in.get();while (ch ! ch ! \n){s ch;ch in.get();}return in;}istream getline(istream in, string s){s.clear();char ch;ch in.get();while (ch ! \n) //区别在于读到空格不结束{s ch;ch in.get();}return in;}void print(const string s1) //输入时用于清空字符串{for (int i 0; i s1.size(); i){cout s1[i] ; //s1不可修改所以[]需要有两个版本}cout endl;string::const_iterator it s1.begin(); //s1不可修改所以定义const迭代器和重载begin和endwhile (it ! s1.end()){cout *it ;it;}cout endl;}bool operator(const string s1, const string s2){size_t i1 0, i2 0;while (i1 s1.size() i2 s2.size()){if (s1[i1] s2[i2]){return true;}else if (s1[i1] s2[i2]){return false;}else{i1;i2;}}//abc abc false//abcd abc true//abc abcd falseif (i1 s1.size()){return false;}else{return true;}}bool operator(const string s1, const string s2){size_t i1 0, i2 0;while (i1 ! s1.size() i2 ! s2.size()){if (s1[i1] ! s2[i2]){return false;}else{i1;i2;}}if (i1 s1.size() i2 s2.size()){return true;}else{return false;}}bool operator!(const string s1, const string s2){return !(s1 s2);}bool operator(const string s1, const string s2){return (s1 s2 || s1 s2);}bool operator(const string s1, const string s2){return !(s1 s2);}bool operator(const string s1, const string s2){return !(s1 s2);}string operator(const string s1, const char* str){string ret s1; //存在深拷贝对象尽量少用ret str;return ret;}void test_string1(){string s1(hello);s1[0] x;string s2(s1);//若是编译器默认实现的拷贝构造则为浅拷贝————导致1、同一块空间析构两次 2、一个对象修改值另一个随之改变string s3(hello world);s1 s3;s1.push_back( );s1.append(by);s1 ;s1 cplusplus;}void test_string2(){string s1;string s2(s1);//s1 x;s1 hello;s1.resize(2);s1.resize(8, x);}void test_string3(){string s1(hello world);//遍历字符串————三种方式普通for循环、迭代器、范围forfor (int i 0; i s1.size(); i){cout s1[i] ;}cout endl;string::iterator it s1.begin(); //指定iterator在string类域中而不在全局中while (it ! s1.end()){cout *it ;it;}cout endl;for (auto e : s1) //语法上支持迭代器、迭代器命名规范即可使用范围for--本质上是替换为迭代器{cout e ;}cout endl;print(s1);}void test_string4(){string s1(hello world);s1.insert(5, x);s1.push_back(!);s1.insert(0, );s1.insert(0, hehe);s1.erase(0, 5);cout s1.c_str() endl;}void test_string5(){string s1(hello world);s1.resize(20);s1[18] x;cout s1 endl; //区别在于输出了_size个字符cout s1.c_str() endl; //遇到\0就终止string s2(hehe);cin s2;cout s2 endl;}void test_string6(){string s1;//cin s1; //输入带空格的字符//cout s1 endl;getline(cin, s1);cout s1 endl;} }int main() {//yfy::test_string1();//yfy::test_string2();//yfy::test_string3();//yfy::test_string4();//yfy::test_string5();yfy::test_string6();return 0; }三、关于string对象的大小 在 Visual Studio 2019 中当 basic_string 的长度小于等于 15 个字符时标准库会将字符串的字符数组存储在 basic_string 对象内部的一个名为 buf 的字符数组中而不是动态分配内存。这个 buf 数组的大小是 16 个字节其中包括 15 个字符和一个用于存储字符串结尾的 null 字符的位置。 因此如果你在 VS2019 的 32 位环境下创建一个 basic_string 对象它的大小可能是 28 个字节。其中16 个字节用于存储 buf 数组4 个字节用于存储指向 buf 数组的指针4 个字节用于存储 size_4 个字节用于存储 capacity_。但需要注意的是这个大小可能因为编译器版本、编译器选项等因素而有所不同。 四、 浅拷贝缺陷的解决方案 下面我们再来回顾一下浅拷贝 C中浅拷贝是指当对象被复制时只复制指向数据的指针而不是数据本身。这意味着当一个对象的内容发生更改时所有指向该对象的指针都会受到影响从而导致潜在的内存泄漏和程序错误例如对象的生命周期结束时多次调用析构函数。 4.0 浅拷贝改深拷贝 浅拷贝的缺陷示例 class MyClass { public:MyClass(int size) {m_data new int[size];m_size size;}~MyClass() {delete[] m_data;} private:int* m_data;int m_size; };MyClass obj1(10); MyClass obj2 obj1;在这个例子中当obj2被复制为obj1时m_data指针也被复制这意味着两个对象都指向相同的内存地址。如果一个对象更改了其内部数据另一个对象也会受到影响。 要解决这个问题可以使用深拷贝来复制对象。深拷贝不仅复制指针而且复制指针所指向的数据本身因此每个对象都有其自己的内存空间。 以下是深拷贝的示例实现 class MyClass { public:MyClass(int size) {m_data new int[size];m_size size;}~MyClass() {delete[] m_data;}// 拷贝构造函数MyClass(const MyClass other) {m_size other.m_size;m_data new int[m_size];memcpy(m_data, other.m_data, m_size * sizeof(int));}// 赋值操作符MyClass operator(const MyClass other) {if (this ! other) {delete[] m_data;m_size other.m_size;m_data new int[m_size];memcpy(m_data, other.m_data, m_size * sizeof(int));}return *this;} private:int* m_data;int m_size; };MyClass obj1(10); MyClass obj2 obj1; // 使用拷贝构造函数进行深拷贝 MyClass obj3(5); obj3 obj1; // 使用赋值操作符进行深拷贝4.1 引用计数 引用计数是一种内存管理技术它通过跟踪对象被引用的次数来决定何时释放内存。当对象被创建时引用计数被初始化为1。每当对象被复制时引用计数也会增加1。当对象不再被使用时引用计数会减少1。当引用计数为0时内存被释放。 class MyClass { public:MyClass(int size) {m_data new int[size];m_size size;m_refCount new int(1);}~MyClass() {if (--(*m_refCount) 0) {delete[] m_data;delete m_refCount;}}// 拷贝构造函数MyClass(const MyClass other) {m_data other.m_data;m_size other.m_size;m_refCount other.m_refCount;(*m_refCount);}// 赋值操作符MyClass operator(const MyClass other) {if (this ! other) {if (--(*m_refCount) 0) {delete[] m_data;delete m_refCount;}m_data other.m_data;m_size other.m_size;m_refCount other.m_refCount;(*m_refCount);}return *this;} private:int* m_data;int m_size;int* m_refCount; };MyClass obj1(10); MyClass obj2 obj1; MyClass obj3(5); obj3 obj1;在这个例子中引用计数被实现为一个指向整数的指针它被存储在对象中。每当对象被复制时引用计数都会增加1。当对象不再被使用时引用计数会减少1。当引用计数为0时内存被释放。这种方法可以避免浅拷贝的问题并且在对象被复制时不需要进行大量的数据复制。但是引用计数需要额外的内存空间来存储计数器而且在多线程环境下需要进行额外的同步和保护。 引用计数指针不能直接写成int类型因为这样会导致内存管理出现问题。 如果我们将引用计数实现为一个普通的int类型那么当对象被复制时每个副本都将拥有一个独立的引用计数。这意味着如果其中一个副本被销毁它的引用计数会减少但是其它副本的引用计数并不会减少这会导致内存泄漏。 使用一个指向int的指针可以解决这个问题。每个副本都可以共享同一个引用计数这样当其中一个副本被销毁时它的引用计数会减少而其它副本的引用计数也会随之减少从而避免了内存泄漏的问题。 4.2 写时拷贝 写时拷贝Copy on Write简称COW是一种内存管理技术它允许多个对象共享同一块内存在对象被修改时才进行复制从而减少内存的消耗。 在COW中当一个对象被拷贝时只有指向原始对象的引用被复制而不是整个对象。在拷贝对象被修改时COW会将原始对象复制到一个新的内存地址并且将引用指向新的内存地址。这个过程是透明的使用者无需关心内部实现。 COW实现 #include iostream #include string.hclass MyString { public:MyString(const char* str) {m_data new char[strlen(str) 1];strcpy(m_data, str);m_refCount new int(1);}~MyString() {if (--(*m_refCount) 0) {delete[] m_data;delete m_refCount;}}// 拷贝构造函数MyString(const MyString other) {m_data other.m_data;m_refCount other.m_refCount;(*m_refCount);}// 赋值操作符MyString operator(const MyString other) {if (this ! other) {if (--(*m_refCount) 0) {delete[] m_data;delete m_refCount;}m_data other.m_data;m_refCount other.m_refCount;(*m_refCount);}return *this;}// 修改字符串时进行复制void ModifyString() {if (*m_refCount 1) {char* newData new char[strlen(m_data) 1];strcpy(newData, m_data);--(*m_refCount);m_data newData;m_refCount new int(1);}} private:char* m_data;int* m_refCount; };int main() {MyString str1(hello);MyString str2 str1;std::cout str1: str1 std::endl;std::cout str2: str2 std::endl;str2.ModifyString();std::cout str1: str1 std::endl;std::cout str2: str2 std::endl;return 0; }在这个例子中MyString类中的m_data指向一个包含字符串的内存块m_refCount存储引用计数。在拷贝对象时只有指向m_data的指针被复制m_refCount也被增加。当对象被修改时如果引用计数大于1则表示有多个对象共享同一块内存此时MyString类会将原始数据复制到一个新的内存地址并将指针指向新的内存地址。这个过程是透明的使用者无需关心内部实现。 在上述示例代码中ModifyString()函数模拟了对字符串进行修改的操作如果对象被多个引用共享则会将数据复制到一个新的内存地址并将引用指向新的内存地址。这样其他对象的数据不会被修改也就避免了浅拷贝带来的问题。 COW技术的缺点是它会增加额外的开销因为每次修改对象时都需要检查引用计数并复制数据。因此COW适用于多读少写的场景在写操作比较频繁的场景中可能会带来性能问题。
http://www.laogonggong.com/news/115372.html

相关文章:

  • 住房和城乡建设部官方网站大神部落 网站建设
  • 做网站找那家公司好网站建设完成
  • 网站前台登陆页面怎么改wordpress jquery版本
  • 免费域名申请哪个网站好成品门户网站源码免费
  • 公司网站建设个人总结建一个app和网站那个比较好
  • 做视频网站的服务器电子商务网站开发实务
  • 公司的网站怎么做推广网站模板源代码
  • 公司网站名词解释wordpress timeline 修改
  • 好的做蛋糕网站北京旅游外贸网站建设
  • 网站后台怎样登陆微商城手机网站制作
  • 哪些网站可以做代理群晖做网站连接数据库
  • 网站建设分析案例做网站阿里云记录值怎么填
  • 深圳市企业网站seo联系方式松江网站建设多少钱
  • 北京网站建设熊掌号做违法网站会怎么样
  • 成都企业网站建设费用企业咨询服务有限公司
  • iis 建立默认网站邹平做网站的公司有哪些
  • 怎么查网站是哪家制作公司做的包装设计网站是什么样子的
  • 动易网站模板中国商业数据网
  • 河南省两学一做网站佛山网站建设拓客科技
  • 网页设计与网站建设案例课堂备案网站电子照幕布
  • 江门做网站哪家好电脑版传奇网站
  • 怎样才能建设只是于自己的网站企业门户网站建设
  • 百度云主机上装网站wordpress 心情评论插件
  • 品牌网站制作网站公司网络营销与策划形考任务四答案
  • 上饶哪有做网站的公司凉山建设局网站
  • 定制网站建设需要多少钱网站的备用金怎么做凭证
  • 极速网站建设服务商wordpress仿盗
  • 个人网站建设的背景长沙房地产信息网官网
  • 网站建设 我们是专业的湖南系统开发
  • 做网站需要多大空间建站推荐