在构造函数中分配共享指针(boost),单元测试

4
我有一个C++类(在dll项目中),其成员变量是指向其他类对象的boost::shared_ptr。将它们分配给类构造函数内部还是有一个单独的init()函数更好?
我假设boost::shared_ptr中T指针的默认值为NULL。因此,如果我在构造函数内不做任何事情,在调用Init()函数之前,boost::shared_ptr的get()将返回NULL。
另外,当赋值语句中new出现内存分配问题时,应该在Init中捕获异常,还是告诉调用者捕获该异常?boost::shared_ptr a(new T);
是否有标准方法来模拟单元测试中的内存分配异常,并查看所有对象是否正确释放?
3个回答

2
我有一个C++类(在dll项目中),其成员变量是指向其他类对象的boost::shared_ptrs。最好在类构造函数内分配它们,而不是使用单独的init()函数。如果使用后者,则意味着对象在构造后无效,因此需要保留状态标志以指示是否调用了init()函数,并在调用任何公共方法时检查该标志并为未初始化的对象执行适当操作。
我假设boost::shared_ptr内部指向T的默认值为NULL。所以如果我在构造函数内什么也不做,那么在调用Init()函数之前,boost::shared_ptr的get()将返回NULL。
是的:shared_ptr的默认构造函数将其初始化为NULL。
当赋值语句中new出现内存分配问题时会发生什么?应该在Init()中捕获异常还是告诉调用者捕获该异常?boost::shared_ptr a( new T);
如果您有一个构造函数:那么已构造的所有成员都将通过析构函数正确地销毁,而未初始化的对象将不会被触及,当前对象的内存将被释放,就像从未分配过一样(这是使用初始化列表的另一个好理由)。
如果使用init():则必须捕获异常正确清除对象并释放内存。根据对象的复杂程度,您可以在init中完成此操作(但很难正确执行),或者调用者必须执行此操作。之后,您应该像从构造函数抛出异常一样做(因此取决于用法)。
是否有标准方法在单元测试中模拟内存分配异常?并查看所有对象是否正确释放。
您可以使用工厂对象来分配对象。将工厂对象传递给构造函数。当您想要在构造过程中模拟异常时,请传递一个生成相应异常的模拟工厂。

1

使用初始化列表进行成员赋值。它们是首选方法(有时甚至是唯一的方法)。

如果在对象创建期间出现问题,请抛出异常并退出。否则,您将需要维护一个valid标志,每次调用成员函数时都必须检查正在使用的对象是否有效。

您始终可以捕获异常并报告它们。如果分配失败,则最好不要抛出异常,而应在析构函数中处理它,报告它并继续执行。


1

关于第一个问题,您最好使用初始化列表来确保指针有效。如果不这样做,那么在使用指针之前,您可能需要始终检查指针是否有效;特别是如果将其留给调用代码来调用init()

另一方面,如果它们只能通过调用纯虚函数来设置,那么您可能无法这样做,这种情况下,您将不得不使用init()方法。

在初始化时,我认为您可以捕获并重新抛出异常,也可以简单地不捕获。无论哪种方式,我认为调用者捕获内存分配异常会更好。

我可以理解为什么您想尝试在其中一个对象上调用init()时测试内存分配异常。在这里的一种方法可能是替换一个总是在调用init()时抛出异常的存根对象。

我不明白为什么您要检查共享指针的释放(原始指针可能)。毫无疑问,共享指针的重点在于,只要您能够引用共享指针,就意味着您正在共享它,因此它尚未被销毁。

也许您可以检查引用计数,或者在指针对象被设置后(在销毁时)清除信号量,以便您的测试程序可以监视。我错过了什么重点吗?

谢谢你的回答,Robin。我在单元测试时应该使用引用计数。我也喜欢创建存根的想法。 - Kamal
@Kamal,如果稍后初始化的成员抛出异常,编译器将调用任何已初始化成员的析构函数。 - Johannes Schaub - litb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接