可以做网站的域名后缀,网站 ca证书怎么做,嘉定网站建站,wordpress 修改目录权限设置//先构造#xff0c;再拷贝构造//利用hello这个字符串创建了一个临时对象//并复制给了s3//这一步实际上new了两次String s3 hello;
背景需求#xff1a; 这个隐式创建的字符串出了该行就直接销毁掉#xff0c;效率比较低 可以让_pstr指向这个空间… //先构造再拷贝构造//利用hello这个字符串创建了一个临时对象//并复制给了s3//这一步实际上new了两次String s3 hello;
背景需求 这个隐式创建的字符串出了该行就直接销毁掉效率比较低 可以让_pstr指向这个空间可以让匿名对象的指针指向nullptr这样析构函数判断是nullptr对于匿名对象就不会处理。 但是不能直接在拷贝构造函数中直接进行操作因为可能有的并不是匿名对象的情况直接将原本的销毁不合适
能对表达式取地址的称为左值不能取地址的称为右值。
1//error取不到地址
hello; //文字常量区可以取地址补充如果文字是相同的会存在一个地方
(a b);//error String(hello); // error
右值存储的位置
右值可以存储在内存中比较复杂的或者放内存中性能更佳的时候。
右值也可以存在寄存器中简单的变量往往放在寄存器中。
区分左右值不能通过存储位置和等号左右进行判断
非const左值引用只能绑定到左值不能绑定到右值也就是非const左值引用只能识别出左值。
const左值引用既可以绑定到左值也可以绑定到右值也就是表明const左值引用不能区分是左值还是右值。
所以接着上面就是我们需要确定只要是右值引用就可以使用特别的右值拷贝构造而左值就是普通的拷贝构造函数。 右值引用
右值引用不能绑定到左值但是可以绑定到右值也就是右值引用可以识别出右值 String(String rhs): _pstr(rhs._pstr)//浅拷贝{cout String(String) endl;rhs._pstr nullptr;} 也就是说在有移动构造的时候对于临时变量就不再调用以前的复制运算符函数
加上编译器的去优化参数 -fno-elide-constructors
发现没有再调用拷贝构造函数而是调用了移动构造函数。
如果还没有就再加上 cstd c11
移动构造函数的特点
1. 四大金刚只要是显示定义一个的时候编译器就不再提供移动构造函数
2. 显示定义了拷贝构造没有移动构造就会调用拷贝构造临时
3. 显示定义了移动构造右值复制使用移动构造。
总结移动构造函数优先级高于拷贝构造函数。也就是会尝试移动函数不行再常规 移动赋值函数
String operator(String rhs){if(this ! rhs){delete [] _pstr;//浅拷贝_pstr rhs._pstr;rhs._pstr nullptr;cout String operator(String) endl;}return *this;
}
其实这地方还有有点容易混淆的因为
String s hello; 移动构造函数
String s String(hello);移动构造函数
s String(xixi) 移动赋值运算符函数
移动赋值运算符函数特点和移动拷贝构造函数相同。
总结
拷贝构造和赋值运算函数具有复制控制语义的函数
移动构造和移动复制有移动语句的函数移交控制权移交控制权
移动语义函数优于控制语义函数 String s1(hello);//右值复制给左值肯定不是同一个对象s1 String(world);//创建了两个内容相同的临时对象也不是同一对象String(wangdao) String(wangdao);
std::move函数
背景需求有时候可能必须需要用到右值所以需要将左值转换为右值 int a 1(std::move(a)); //error左值转成了右值int r std::move(a);
【注意】
值显式转换为右值后原来的左值对象就无法正常工作了必须要重新赋值才可以继续使用。 但是通过输出流运算符输出s1的 _pstr依然造成了程序的中断所以说明对std::move(s1)的内容进行修改会导致s1的内容也被修改。
std::move的本质是在底层做了强制转换并不是像名字表面的意思一样做了移动
这就更是说明了move只是欺骗系统说这是一个右值让系统当作一个右值处理并且将控制权也都交了出去对于这个‘右值’的处理就是对于本身的处理。
【注意】在这个时候可能在移动赋值函数中如果是使用move导致的如果去掉自复制判断自复制情况下就会出现问题所以必须加上自复制判断。
String operator(String rhs){if(this ! rhs){delete [] _pstr;//浅拷贝_pstr rhs._pstr;rhs._pstr nullptr;cout String operator(String) endl;}return *this;
} 右值引用本身的性质
int func(){return 10;
}void test1(){// func(); //无法取址说明返回的右值引用本身也是一个右值int ref func();ref; //可以取址此时ref是一个右值引用其本身是左值
}
注意区分下列的情况。
第一二种情况返回的都是副本临时值都是不能取地址的第三种情况是全局变量的引用可以取地址第四种情况是返回一个右值引用是不可以取地址的但是如果是是在程序中定义的一个右值引用的话是可以进行取地址操作的原因就是下述所讲有名字的右值引用是左值无名字是右值。 有名字右值引用是左值没有名字还是左值
String str2(wangdao);
String func2(){String str1(wangdao);str1.print();return str1; //对于一个将亡的对象而言是调用移动构造//return str2;//长期存在的就调用拷贝构造函数
}void test2(){func2();//func2(); //error,右值String ref func2();ref; //右值引用本身为左值
}
在上述代码中return语句是调用移动构造语句而不是拷贝构造语句
return
总结返回将亡值对象使用移动构造否则调用拷贝构造 资源管理
背景需求程序的出口比较多如果在某一个出口忘记将资源释放的时候不能很好的将资源释放
所以说想到可以通过一个类进行封装保护实现就像是运算符重载一章中的middleLayer
class SafeFile
{
public://在构造函数中初始化资源托管资源SafeFile(FILE * fp): _fp(fp){cout SafeFile(FILE*) endl;}//提供方法访问资源void write(const string msg){fwrite(msg.c_str(),1,msg.size(),_fp);}//利用析构函数释放资源~SafeFile(){cout ~SafeFile() endl;if(_fp){fclose(_fp); cout fclose(_fp) endl;}}
private:FILE * _fp;
};void test0(){string msg hello,world;SafeFile sf(fopen(wd.txt,a));sf.write(msg);
}
同时也像是middleLayer中的担心会出现多次释放的情况所以也是直接在创建对象的时候就是用fopen而不是再赋给一个有名的指针。
RAII技术
RAII类的常见特征
RAII常见的特征
1.构造函数中托管资源析构中释放资源
2.不允许复制和赋值对象语义
3.提供若干访问方法 -*读写
值语义可以进行复制或赋值两个变量的值可以相同
对象语义不允许复制或者赋值
【联系】上面刚说了移动语义和赋值控制语义 常用手段 将拷贝构造函数与赋值运算符函数设置为私有的将拷贝构造函数与赋值运算符函数delete使用继承的思想将基类的拷贝构造函数与赋值运算符函数删除或设为私有让派生类继承基类。 使用最保险的就是将函数删除的方法 RAII类的模拟
借助于上面对于智能指针思想的描述的实现。
template class T
class RAII
{
public://1.在构造函数中初始化资源托管资源RAII(T * data): _data(data){cout RAII(T*) endl;}//2.在析构函数中释放资源~RAII(){cout ~RAII() endl;if(_data){delete _data;_data nullptr;}}//3.提供若干访问资源的方法T * operator-(){return _data;}T operator*(){return *_data;}T * get() const{return _data;}void set(T * data){if(_data){delete _data;_data nullptr;}_data data;}//4.不允许复制或赋值RAII(const RAII rhs) delete;RAII operator(const RAII rhs) delete;
private:T * _data;
}; 智能指针
//memory头文件中
//std::auto_ptr c0x 不安全在c17中已经丢弃
//std::unique_ptr c11
//std::shared_ptr c11
//std::weak_ptr c11 auto_ptr
在其中auto_ptr的赋值运算符函数和拷贝构造函数是可以使用的。
auto_ptr的拷贝构造实际上却是移动复制
template class _Tp
class auto_ptr {
public://拷贝构造auto_ptr(auto_ptr __a) __STL_NOTHROW //ap2的_M_ptr 被赋值为 ap调用release函数的返回值: _M_ptr(__a.release()) {}//ap调用release函数_Tp* release() __STL_NOTHROW {//用局部的指针__tmp接管ap的指针所指向的资源_Tp* __tmp _M_ptr;_M_ptr nullptr; //将ap底层的指针设为空指针return __tmp;//返回的就是原本ap管理的资源的地址}private:_Tp* _M_ptr;
}; auto_ptrint ap2(ap);cout *ap2: *ap2 endl; //okcout *ap: *ap endl; //error因为底层是移动构造
该拷贝出现隐患被丢弃 unique_ptr(重要)
1. 独享所有权的指针 不允许进行复制或者赋值对象语义但是具有移动语义
2. 作为容器元素
当作为容器的元素传入的时候如果是直接传入一个左值的话会调用复制运算符函数但是函数已经被删除但是移动复制没有删除所以说使用移动复制函数移交管理权。
就是使用匿名对象move函数但是实际上的所有权被移交了所以原来的指针失效不用
vectorunique_ptrPoint vec;unique_ptrPoint up4(new Point(10,20));//up4是一个左值//将up4这个对象作为参数传给了push_back函数会调用拷贝构造//但是unique_ptr的拷贝构造已经删除了//所以这样写会报错vec.push_back(up4); //errorvec.push_back(std::move(up4)); //ok但是不用还是移交了管理权vec.push_back(unique_ptrPoint(new Point(1,3))); //ok share_ptr(重要)
背景需求 unique_ptr是独占所有权的智能指针有时候需要共享有了share_ptr
原理类似cowstring的原理就是有一个引用计数
特征
1. 共享所有权可以复制或者赋值但是具有移动语义有移动构造和移动复制
2. 可以作为容器元素
如果是把左值属性的指针传进去会出现复制操作。use_count会加1
但是会出现循环引用的问题
class Child;class Parent
{
public:Parent(){ cout Parent() endl; }~Parent(){ cout ~Parent() endl; }//只需要Child类型的指针不需要类的完整定义shared_ptrChild _spChild;
};class Child
{
public:Child(){ cout child() endl; }~Child(){ cout ~child() endl; }shared_ptrParent _spParent;
};
shared_ptrParent parentPtr(new Parent());
shared_ptrChild childPtr(new Child());
//获取到的引用计数都是1
cout parentPtr.use_count(): parentPtr.use_count() endl;
cout childPtr.use_count(): childPtr.use_count() endl; parentPtr-_spChild childPtr;
childPtr-spParent parentPtr;
//获取到的引用计数都是2
cout parentPtr.use_count(): parentPtr.use_count() endl;
cout childPtr.use_count(): childPtr.use_count() endl; 当栈上的指针删除的时候还会有引用计数都是1 解决这个问题引入弱引用指针。因为上述的环在有栈上的指针的时候只要是有一个引用计数不为2就可以解除这个环。因为当栈上指针不再指向的时候就会变成0销毁后另外一个引用也变为0 week_ptr弱引用的指针
不会增加引用计数
week_ptr是一个弱引用的指针不会增加引用计数。
对于上述问题的解决办法将Parent类中的shared_ptr类型指针换成weak_ptr week_ptr指向不会增加引用计数 可能出现的问题就是引用计数 减到0 _wpChild指针还指向这个位置会不会出现访问错误问题
实际上week_ptr这个指针不能访问因为是弱引用指针不能访问
weak_ptr知道所托管的对象是否还存活如果存活必须要提升为shared_ptr才能对资源进行访问不能直接访问。
use_count()只能访问share_ptr的指针指向的个数也就是知道week_ptr有没有指向空间
week_ptr的初始化的方式
weak_ptrint wp;//无参的方式创建weak_ptr//也可以利用shared_ptr创建weak_ptr
weak_ptrint wp2(sp);
可能不成功当没有空间可以管理的时候否则有另外一个shared_ptr指针指向。
shared_ptrint sp2 wp.lock();
if(sp2){
cout 创建shared成功 endl;
cout *sp2 endl;
}else{
cout 创建shared失败托管的空间已经被销毁或者没有资源可以管理 endl;
}
expired函数
bool flag wp.expired();
if(flag){
cout 托管的空间已经被销毁 endl;
}else{
cout 托管的空间还在 endl;
} 删除器
unique指针的模板参数
背景需求 默认的删除器使用的是delete不能回收fopen打开的文件因为是delete而不能是fclose。
问题描述如果是使用原本的删除器继续使用delete不close写的内容可能不能刷新缓冲区如果再手动close会导致double free问题因为delete底层本身就是free函数。
所以说需要自定义删除器将删除器中的处理设置为fclose。 struct FILECloser{
void operator()(FILE * fp){if(fp){fclose(fp);cout fclose(fp) endl;}
}
};
void test1(){
string msg hello,world\n;
unique_ptrFILEFILECloser up(fopen(res2.txt,a));
//get函数可以从智能指针中获取到裸指针
fwrite(msg.c_str(),1,msg.size(),up.get());
}
shared_ptr(作为构造函数参数) 智能指针误用
将一个资源原生指针交给两个指针指针管理
void test0(){
//需要人为注意避免
Point * pt new Point(1,2);
unique_ptrPoint up(pt);
unique_ptrPoint up2(pt);//error出现double free
}//unique是独占的因此不能共同管理一个对象。
void test2(){
Point * pt new Point(10,20);
shared_ptrPoint sp(pt);
shared_ptrPoint sp2(pt);//error
}//虽然说shared_ptr是可以共享的但是也必须是使用赋值和复制的行为才是可以的否则不可以 这个地方addPoint如果是放回的是Point*就会出现sp3和sp管理一个Point*原生指针
如果是返回的是shared_ptrPoint(this)这种情况也是sp和匿名对象共用this这个point对象指针
这个地方就是用到动态内存管理中的辅助类enable_shared_from_this中的成员函数shared_from_this来实现功能。就是使用继承的方式在该类中可以使用类的成员函数。