C++类中的指针成员变量(生命期紧随该类)的new操作,应该在类的构造函数中?还是需要一个Init()函数,也就是在构造函数外?为了解决这个问题,写个例子测试下。。
首先定义三个类,没有什么重要意义
class A{ public: A(){ std::cout << "A()" <<std::endl; } ~A(){ std::cout << "~A()" <<std::endl; } }; class B{ public: B(){ std::cout << "B()" <<std::endl; } ~B(){ std::cout << "~B()" <<std::endl; } }; class C{ public: C(){ std::cout << "C()" <<std::endl; } ~C(){ std::cout << "~C()" <<std::endl; } };
现在抛出问题,下面两段代码,那种的处理方式比较好呢?
代码1:
代码2:class Test{ public: Test(){ a_ = new A; b_ = new B; c_ = new C; } ~Test(){ delete a_; delete b_; delete c_; } private: A* a_; B* b_; C* c_; };
class Test{ public: Test(){} void Init(){ a_ = new A; b_ = new B; c_ = new C; } ~Test(){ delete a_; delete b_; delete c_; } private: A* a_; B* b_; C* c_; };
对于上面的两个代码的调用如下:
//代码1执行如下: Test * test1 = new Test; delete test1; //代码2执行如下: Test * test2 = new Test; test2->Init(); delete test2;
输出的结果都如下:
A() B() C() ~A() ~B() ~C()
到目前为止,好像并不能发现两段代码有什么不同之处! 但是别着急。
看看异常出现的时候(throw模拟异常),会有怎样的结果:看看以下这两个代码
代码1:
输出结果:class Test{ public: Test(){ a_ = new A; b_ = new B; throw std::string("构造函数中抛出异常"); c_ = new C; } ~Test(){ delete a_; delete b_; delete c_; } private: A* a_; B* b_; C* c_; }; int main(int argc, char *argv[]) { Test * test = NULL; try { test = new Test; } catch (const std::string& str) { std::cout << str << std::endl; } delete test; std::cout << "main end" << std::endl; return 0; }
A() B() 构造函数中抛出异常 main end
代码2:
输出结果:class Test{ public: Test(){} void Init(){ a_ = new A; b_ = new B; throw std::string("Init()中抛出异常"); c_ = new C; } ~Test(){ delete a_; delete b_; delete c_; } private: A* a_; B* b_; C* c_; }; int main(int argc, char *argv[]) { Test * test = NULL; try { test = new Test; test->Init(); } catch (const std::string& str) { std::cout << str << std::endl; } delete test; std::cout << "main end" << std::endl; return 0; }
A() B() Init()中抛出异常 ~A() ~B() ~C() new(4421,0x7fffd62833c0) malloc: *** error for object 0x7fffd6110010: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug //小编注,错误原因是c_没有初始化,是一个随机数,delete会产生崩溃
分析上面的例子,很明显可以发现:
代码1:调用了A、B的构造函数,但是没有调用A、B的析构函数,程序运行结束,造成了内存泄漏;
代码2:调用了A、B的构造函数,调用了A、B、C的析构函数。因为c_没有调用构造函数,却去析构它(此时c_是随机值),因此程序崩溃。
到了这里,你可能会觉得,那两种方式都有问题的,那该怎么写呢?? 但是,还是别着急,分析上面两个代码出错的原因,可以联想到delete NULL,是的,C++的delete兼容NULL,析构空并不会报错!!
所以上面的代码2可以修改成这样:
输出结果如下:class Test{ public: Test(){ a_ = NULL; b_ = NULL; c_ = NULL; } void Init(){ a_ = new A; b_ = new B; throw std::string("Init()中抛出异常"); c_ = new C; } ~Test(){ delete a_; delete b_; delete c_; } private: A* a_; B* b_; C* c_; }; int main(int argc, char *argv[]) { Test * test = NULL; try { test = new Test; test->Init(); } catch (const std::string& str) { std::cout << str << std::endl; } delete test; std::cout << "main end" << std::endl; return 0; }
A() B() Init()中抛出异常 ~A() ~B() main end
perfect!!!完美解决,没有内存泄漏,程序没有崩溃!!!
最后的最后,总结下,构造函数中尽量不要有new的操作,new的操作可以定义一个Init()来单独处理,代码如下:最后再试试上面的代码1,看能不能通过赋空解决呢?
运行一下,还是老样子(内存泄漏),朽木不可雕也,抛弃!class Test{ public: Test(){ a_ = NULL; b_ = NULL; c_ = NULL; a_ = new A; b_ = new B; throw std::string("构造函数中抛出异常"); c_ = new C; } ~Test(){ delete a_; delete b_; delete c_; } private: A* a_; B* b_; C* c_; }; int main(int argc, char *argv[]) { Test * test = NULL; try { test = new Test; } catch (const std::string& str) { std::cout << str << std::endl; } delete test; std::cout << "main end" << std::endl; return 0; }
class Test{ public: Test(){ a_ = NULL; b_ = NULL; c_ = NULL; } void Init(){ a_ = new A; b_ = new B; c_ = new C; } ~Test(){ delete a_; delete b_; delete c_; } private: A* a_; B* b_; C* c_; };