这几天在看effective C++3rd,这本书算是比较经典的一本入门C++的书了。虽然年代比较久远书中讲的好多模式已经被的新特性取代了,但是从这些旧的模式中可以了解到一些C++新特性设计的初衷,也算是窥探到了C++发展的一角吧。
所以在此记录一下学习过程中那些被C++11新特性取代的旧模式吧(C++11后面的特性我还不是很了解,以后学到了再补充吧)
1、阻止编译器自动生成函数
编译器在一些情况下会为自定义类自动生成默认构造函数、拷贝构造函数和运算符,移动构造函数和运算符(C++11新特性)。但是有些自定义类不希望用户进行一些操作,就应该在设计时就考虑到这种情况。
比如:一个类Uncopyable是一个机密信息,不允许被拷贝,所以没有在类中设计拷贝控制函数。但是如果恰好写了赋值操作,则编译器会自动生成一个拷贝赋值运算符,下面代码是运行正确的,这显然与设计初衷不符。
class Uncopable; Uncopable uncopy,user; user=uncopy;
为了避免这种情况的发生,我们应该阻止编译器自动生成某些函数。
旧设计模式:将目标函数声明为private并且不定义
C++11新特性:将函数定义为delete,定义为delete的函数编译器不会自动为其合成相关函数并且也不允许进行相关操作。
2、阻止类被继承
派生类继承自一个带有非虚析构函数的类可能导致在销毁派生类对象时的内存泄漏,因为派生类对象销毁时可能没有销毁继承自基类的子成员。并且有一些类设计之初就不打算作为基类让其他类继承,如果这些类被继承的话会导致上述的内存泄漏。例如下列代码会导致A的对象没有被完全释放。
1 struct A{ //未考虑继承 2 A() :x(new int) { cout << "c x" << endl; } 3 ~A() { delete x; 4 cout << "d x" << endl; 5 } 6 private: 7 int* x; 8 }; 9 struct B :A10 {11 B() :A(), y(new double) { cout << "c y" << endl; }12 ~B() { delete y;13 cout << "d y" << endl;14 }15 private:16 double* y;17 };18 19 20 int main()21 {22 B *b = new B;23 A *a;24 a=b;25 delete a; //内存泄漏26 return 0;27 }
输出结果:
c x c y d x
上述代码因为类A不含虚函数所以没有多态特性,所以删除指针a时只调用A的析构函数,销毁了A的成员x,B的成员y没有销毁导致内存泄漏。
如果即使不考虑继承但是也将析构函数设为virtual,则会增加类对象的体积并且使其具有不可移植性(会建立虚函数表,此部分看effective C++3rd 条款7)。旧的C++似乎无法阻止这种情况的发生,但是C++11弥补了这项缺陷。
C++11新特性:将类设为final,设为final的类不予许作为基类。
3、确保内存资源被及时释放(以对象管理资源)
动态分配内存后容易出现忘记释放此块内存,控制内存的变量被销毁但是内存却没有释放等情况。如
1 Investment *createInvestment()2 {3 ...创建一个Investment对象并返回指向它的指针;4 }5 int main(){6 Investment* pInv=createInvestment();7 ...8 delete pInv;9 }
函数Investment()返回一个指向Investment的指针,如果在第七行省略的代码中发生了异常、忘记写第8行delete代码或者直接调用createInvestment函数赋值给一个局部指针等行为均可能导致内存泄漏。旧风格的C++代码处理这种情况的一种方式就是建立一个可以管理这些动态资源的类,用析构函数去释放其控制的资源。或者用早期的智能指针auto_ptr和引用计数型智慧指针(RCSP)。当然这些早期智能指针已经被C++11中的智能指针完美包含了。
C++11新特性:智能指针shared_ptr和unique_ptr,以及配合shared_ptr的weak_ptr
①shared_ptr:引用计数型智慧指针的代表并且通过配合weak_ptr可以克服RCSP环状引用(cycles of references)的缺陷。
②unique_ptr:auto_ptr的完全体,克服了不能管理动态数组的缺点。