C++primer-课件-9

整理文档很辛苦,赏杯茶钱您下走!

免费阅读已结束,点击下载阅读编辑剩下 ...

阅读已结束,您可以下载文档离线阅读编辑

资源描述

C++程序设计第13章拷贝控制计算机学院黄章进zhuang@ustc.edu.cn内容13.1拷贝、赋值与销毁13.2拷贝控制与资源管理拷贝控制2拷贝控制成员函数类通过五种特殊的成员函数来进行类对象的拷贝控制(copycontrol)拷贝构造函数(copyconstructor)移动构造函数(moveconstructor)定义用同类型的另一对象来初始化对象时的行为拷贝赋值运算符(copy-assignmentoperator)移动赋值运算符(move-assignmentoperator)定义用对象给同类型的另一对象赋值时的操作析构函数(destructor)定义类的对象销毁时的操作3拷贝、赋值与销毁最基本的拷贝控制操作拷贝构造函数拷贝赋值运算符析构函数C++11引入移动操作(不讲)移动构造函数移动赋值运算符拷贝控制4拷贝构造函数拷贝构造函数是指第一个形参是自身类类型的引用,且任何额外形参都有默认实参的构造函数classFoo{public:Foo();//defaultconstructorFoo(constFoo&);//copyconstructor//...};虽然可以定义接受非const引用形参的拷贝构造函数,但第一个形参几乎总是const的引用拷贝构造函数通常不应是explicit的拷贝、赋值与销毁5合成拷贝构造函数如果没有为类定义拷贝构造函数,编译器会定义一个合成拷贝构造函数不同于默认构造函数,即使有其他构造函数,编译器也会合成一个拷贝构造函数一般地,合成拷贝构造函数会将实参对象的非静态成员逐成员地拷贝到正在创建的对象中对类类型成员,使用其拷贝构造函数来拷贝内置类型的成员被直接拷贝对数组类型成员,逐元素地拷贝;如果元素是类类型,使用元素的拷贝构造函数来拷贝拷贝构造函数6合成拷贝构造函数classSales_data{public://othermembersandconstructorsasbefore//declarationequivalenttothesynthesizedcopyconstructorSales_data(constSales_data&);private:std::stringbookNo;intunits_sold=0;doublerevenue=0.0;};//equivalenttothecopyconstructorthatwouldbesynthesizedforSales_dataSales_data::Sales_data(constSales_data&orig):bookNo(orig.bookNo),//usesthestringcopyconstructorunits_sold(orig.units_sold),//copiesorig.units_soldrevenue(orig.revenue)//copiesorig.revenue{}//emptybody拷贝构造函数7拷贝初始化直接初始化时,编译器使用普通的函数匹配来选择与提供的实参最匹配的构造函数拷贝初始化要求编译器将右侧运算对象拷贝到正在创建中的对象,如果需要还有进行类型转换拷贝初始化通常使用拷贝构造函数来完成stringdots(10,'.');//directinitializationstrings(dots);//directinitializationstrings2=dots;//copyinitializationstringnull_book=9-999-99999-9;//copyinitializationstringnines=string(100,'9');//copyinitialization拷贝构造函数8拷贝初始化拷贝初始化不仅在用=号定义变量时发生,在下列情况下也会发生:将对象作为实参传递给非引用类型的形参拷贝构造函数的形参必须是引用类型从返回类型为非引用类型的函数返回对象用花括号列表初始化一个数组的元素或一个聚合类的成员拷贝构造函数9拷贝初始化的限制当使用的初始值需要explicit构造函数进行类型转换时,直接初始化和拷贝初始化有所区别当传递实参或从函数返回值时,不能隐式地使用explicit构造函数vectorintv1(10);//ok:directinitializationvectorintv2=10;//error:constructorthattakesasizeisexplicitvoidf(vectorint);//f'sparameteriscopyinitializedf(10);//error:can'tuseanexplicitconstructortocopyanargumentf(vectorint(10));//ok:directlyconstructatemporaryvectorfromanint拷贝构造函数10编译器可以绕开拷贝构造函数在拷贝初始化时,编译器允许(但不是必须)跳过拷贝/移动构造函数,直接创建对象。例如,stringnull_book=9-999-99999-9;//copyinitialization编译时可能被改写为stringnull_book(9-999-99999-9);//compileromitsthecopyconstructor拷贝构造函数11练习练习13.4:假定Point是有一个public的拷贝构造函数的类类型,指出下面程序中哪些地方使用了拷贝构造函数:Pointglobal;Pointfoo_bar(Pointarg){Pointlocal=arg,*heap=newPoint(global);*heap=local;Pointpa[4]={local,*heap};return*heap;}拷贝构造函数12拷贝赋值运算符类可以控制其对象如何赋值:Sales_datatrans,accum;trans=accum;//usestheSales_datacopy-assignmentoperator如果类未定义自己的拷贝赋值运算符,编译器会为它合成一个拷贝、赋值与销毁13重载赋值运算符重载运算符(overloadedoperator)本质上是函数,函数名由operator关键字后跟表示要定义的运算符的符号组成运算符函数也有返回类型和形参列表重载运算符的形参表示运算符的运算对象赋值运算符operator=必须定义为成员函数:a=b等价于a.operator=(b)左侧运算对象绑定到隐式的this形参右侧运算对象作为显式形参传递拷贝赋值运算符14重载赋值运算符拷贝赋值运算符接受一个其所在类类型的参数通常返回一个对其左侧运算对象的引用classFoo{public:Foo&operator=(constFoo&);//assignmentoperator//...};拷贝赋值运算符15合成拷贝赋值运算符如果类未定义拷贝赋值运算符,编译器会生成一个合成拷贝赋值运算符(synthesizedcopy-assignmentoperator)将右侧运算对象的每个非静态成员赋给左侧运算对象的对应成员类类型成员通过类的拷贝赋值运算符完成对于数组类型的成员,逐个元素进行赋值返回左侧运算对象的引用拷贝赋值运算符16合成拷贝赋值运算符//equivalenttothesynthesizedcopy-assignmentoperatorSales_data&Sales_data::operator=(constSales_data&rhs){bookNo=rhs.bookNo;//callsthestring::operator=units_sold=rhs.units_sold;//usesthebuilt-inintassignmentrevenue=rhs.revenue;//usesthebuilt-indoubleassignmentreturn*this;//returnareferencetothisobject}拷贝赋值运算符17析构函数析构函数(destructor)释放对象使用的资源,并销毁对象的非static数据成员析构函数是类的成员函数函数名由波浪号(~)接类名构成没有返回值,也不接受参数不能被重载,只有唯一的析构函数classFoo{public:~Foo();//destructor//...};拷贝、赋值与销毁18析构函数完成的工作析构函数首先执行函数体,然后销毁成员析构函数体通常释放对象在生存期分配的所有资源成员按初始化顺序的逆序销毁销毁类类型的成员需要执行成员的析构函数销毁内置类型成员什么也不需要做隐式地销毁内置指针类型的成员不会delete它所指向的对象析构函数19何时调用析构函数无论何时对象被销毁,都会自动调用其析构函数变量在离开其作用域时被销毁当一个对象被销毁时,其成员被销毁容器(无论是标准库容器还是数组)被销毁时,其元素被销毁对于动态分配的对象,当delete指向它的指针时被销毁对临时对象,当创建它的完整表达式结束时被销毁析构函数20何时调用析构函数{//newscope//pandp2pointtodynamicallyallocatedobjectsSales_data*p=newSales_data;//pisabuilt-inpointerSales_dataitem(*p);//copyconstructorcopies*pintoitemvectorSales_datavec;//localobjectvec.push_back(*p);//copiestheobjecttowhichppointsdeletep;//destructorcalledontheobjectpointedtobyp}//exitlocalscope;destructorcalledonitem,andvec//destroyingvecdestroystheelementsinvec当指向一个对象的引用或指针离开作用域时,析构函数不会执行析构函数21合成析构函数当类未定义析构函数时,编译器会为它定义一个合成析构函数(synthesizeddestructor)合成析构函数的函数体为空成员是在析构函数体执行完毕后隐含的析构阶段中被自动销毁classSales_data{public://noworktodootherthandestroyingthemembers,whichhappensautomatically~Sales_data(){}//equivalentsynthesizeddestructor//othermembersasbefore};析构函数22练习练习13.12:下面代码片段中会发生几次析构函数调用?boolfcn(constSales_data*trans,Sales_dataaccum){Sales_dataitem1(*trans),item2(accum);returnitem1.isbn()!=item2.isbn();}析构函数23一些法则基本原则一:如果类需要一个析构函数,几乎可以肯定它也需要一个拷贝构造函数和一个拷贝赋值运算符基本原则二:如果类需要一个拷贝构造函数,几乎可以肯定它也需要一个拷贝赋值运算符,反之亦然拷贝、赋值与销毁24使用=default通过将拷贝控制成员定义为=default来显式要求编译器生成合成的版本在类内用=default修饰成员的声明时,合成的成员函数是隐式内联的如果不希望合成的成员是内联函数,应该在类外定义成员时使用=default拷贝、赋值与销毁26使用=defaultclassSales_data{public://copycontrol;usedefaultsSales_data()=default;Sales_dat

1 / 51
下载文档,编辑使用

©2015-2020 m.777doc.com 三七文档.

备案号:鲁ICP备2024069028号-1 客服联系 QQ:2149211541

×
保存成功