C++成员变量的new操作应该放在构造函数中吗?

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:

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_;
};
代码2:

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!!!完美解决,没有内存泄漏,程序没有崩溃!!!

最后再试试上面的代码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;
}
运行一下,还是老样子(内存泄漏),朽木不可雕也,抛弃!


最后的最后,总结下,构造函数中尽量不要有new的操作,new的操作可以定义一个Init()来单独处理,代码如下:

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_;
};

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值