所以我有这个库代码,请参阅......
class Thing
{
public:
class Obj
{
public:
static const int len = 16;
explicit Obj(char *str)
{
strncpy(str_, str, len);
}
virtual void operator()() = 0;
private:
char str_[len];
};
explicit Thing(vector<Obj*> &objs) : objs_(objs) {}
~Thing() {
for(vector<Obj*>::iterator i = objs_.begin(); i != objs_.end(); ++i) {
delete *i;
}
}
private:
vector<Obj*> objs_;
}
在我的客户代码中......
class FooObj : public Thing::Obj
{
virtual void operator()() {
//do stuff
}
}
class BarObj : public Thing::Obj
{
virtual void operator()() {
//do different stuff
}
}
vector<Objs*> objs;
int nStructs = system_call(*structs);
for(int i = 0; i < nStructs; i++) {
objs.push_back(newFooObj(structs[i].str));
}
objs.push_back(newBarObj("bar1");
objs.push_back(newBarObj("bar2");
Thing thing(objs);
// thing does stuff, including call Obj::()() on the elements of the objs_ vector
我坚持的是异常安全。就目前而言,如果任何Obj构造函数抛出,或者Thing构造函数抛出,则向量中的Objs将泄漏。向量需要包含指向Objs的指针,因为它们是以多态方式使用的。而且,我需要在这里处理任何异常,因为这是从不知道异常的旧代码库中调用的。
我认为,我的选择是:
答案 0 :(得分:5)
答案 1 :(得分:4)
答案3 - 在向量中使用智能指针而不是Obj *。我建议boost::shared_ptr。
答案 2 :(得分:4)
由于您的Thing析构函数已经知道如何清理向量,因此您可以通过RAII解决方案。您可以使用空向量初始化Thing而不是创建Objs的向量,然后将其传递给Thing的构造函数,并添加成员函数以通过指针将新的Objs添加到向量。
这样,如果Obj的构造函数抛出,编译器将自动调用Thing的析构函数,正确销毁已经分配的任何Obj。
Thing的构造函数变为无操作:
explicit Thing() {}
添加push_back
成员:
void push_back(Obj *new_obj) { objs_.push_back(new_obj); }
然后您客户端的分配代码变为:
Thing thing(objs);
int nStructs = system_call(*structs);
for(int i = 0; i < nStructs; i++) {
thing.push_back(newFooObj(structs[i].str));
}
thing.push_back(newBarObj("bar1");
thing.push_back(newBarObj("bar2");
正如另一张海报建议的那样,矢量中的智能指针类型也可以正常工作。只是不要使用STL的auto_ptr
;它不遵循正常的复制语义,因此不适合在STL容器中使用。由Boost提供的shared_ptr
和即将推出的C ++ 0x会没问题。
答案 3 :(得分:1)
Obj的向量对于性能来说可能非常差,因为向量可能必须在调整大小时移动对象并且必须将它们全部复制。指向对象的指针更好。
我已经使用Pointainers来做你需要的事情。这是原始代码。
/*
* pointainer - auto-cleaning container of pointers
*
* Example usage:
* {
* pointainer< std::vector<int*> > v;
* // v can be manipulated like any std::vector<int*>.
*
* v.push_back(new int(42));
* v.push_back(new int(17));
* // v now owns the allocated int-s
*
* v.erase(v.begin());
* // frees the memory allocated for the int 42, and then removes the
* // first element of v.
* }
* // v's destructor is called, and it frees the memory allocated for
* // the int 17.
*
* Notes:
* 1. Assumes all elements are unique (you don't have two elements
* pointing to the same object, otherwise you might delete it twice).
* 2. Not usable with pair associative containers (map and multimap).
* 3. For ANSI-challenged compilers, you may want to #define
* NO_MEMBER_TEMPLATES.
*
* Written 10-Jan-1999 by Yonat Sharon <yonat@@ootips.org>
* Last updated 07-Feb-1999
*
* Modified June 9, 2003 by Steve Fossen
* -- to fix g++ compiling problem with base class typenames
*/
#ifndef POINTAINER_H
#define POINTAINER_H
#ifdef NO_MEMBER_TEMPLATES
#include <functional> // for binder2nd
#endif
template <typename Cnt>
class pointainer : public Cnt
{
public:
// sf - change to fix g++ compiletime errors
#ifdef USE_USING_NOT_TYPEDEF
// I get compile errors with this
using typename Cnt::size_type;
using typename Cnt::difference_type;
using typename Cnt::reference;
using typename Cnt::const_reference;
using typename Cnt::value_type;
using typename Cnt::iterator;
using typename Cnt::const_iterator;
using typename Cnt::reverse_iterator;
using typename Cnt::const_reverse_iterator;
#else
// this way works
typedef typename Cnt::size_type size_type;
typedef typename Cnt::difference_type difference_type;
typedef typename Cnt::reference reference;
typedef typename Cnt::const_reference const_reference;
typedef typename Cnt::value_type value_type;
typedef typename Cnt::iterator iterator;
typedef typename Cnt::const_iterator const_iterator;
typedef typename Cnt::reverse_iterator reverse_iterator;
typedef typename Cnt::const_reverse_iterator const_reverse_iterator;
#endif
typedef pointainer< Cnt > its_type;
pointainer() {}
pointainer( const Cnt &c ) : Cnt( c ) {}
its_type &operator=( const Cnt &c ) { Cnt::operator=( c ); return *this; }
~pointainer() { clean_all(); }
void clear() { clean_all(); Cnt::clear(); }
iterator erase( iterator i ) { clean( i ); return Cnt::erase( i ); }
iterator erase( iterator f, iterator l ) { clean( f, l ); return Cnt::erase( f, l ); }
// for associative containers: erase() a value
size_type erase( const value_type& v )
{
iterator i = find( v );
size_type found( i != end() ); // can't have more than 1
if( found )
erase( i );
return found;
}
// for sequence containers: pop_front(), pop_back(), resize() and assign()
void pop_front() { clean( begin() ); Cnt::pop_front(); }
void pop_back() { iterator i( end() ); clean( --i ); Cnt::pop_back(); }
void resize( size_type s, value_type c = value_type() )
{
if( s < size() )
clean( begin() + s, end() );
Cnt::resize( s, c );
}
#ifndef NO_MEMBER_TEMPLATES
template <class InIter> void assign(InIter f, InIter l)
#else
void assign( iterator f, iterator l )
#endif
{
clean_all();
Cnt::assign( f, l );
}
#ifndef NO_MEMBER_TEMPLATES
template <class Size, class T> void assign( Size n, const T& t = T() )
#else
void assign( size_t n, const value_type& t = value_type() )
#endif
{
clean_all();
Cnt::assign( n, t );
}
// for std::list: remove() and remove_if()
void remove( const value_type& v )
{
clean( std::find( begin(), end(), v ));
Cnt::remove( v );
}
#ifndef NO_MEMBER_TEMPLATES
template <class Pred>
#else
typedef std::binder2nd< std::not_equal_to< value_type > > Pred;
#endif
void remove_if(Pred pr)
{
for( iterator i = begin(); i != end(); ++i )
if( pr( *i ))
clean( i );
Cnt::remove_if( pr );
}
private:
void clean( iterator i ) { delete *i; *i = 0; } // sf add *i = NULL so double deletes don't fail
void clean( iterator f, iterator l ) { while( f != l ) clean( f++ ); }
void clean_all() { clean( begin(), end() ); }
// we can't have two pointainers own the same objects:
pointainer( const its_type& ) {}
its_type& operator=( const its_type& ) { return NULL;} // sf - return null to remove no return value warning..
};
#endif // POINTAINER_H
答案 4 :(得分:0)
而不是指向Obj的指针向量,你总是可以使用Obj的向量。在这种情况下,您需要确保您可以安全地复制Obj(当它包含指针时是危险的)。因为你的Obj只包含一个固定大小的char数组,所以它应该是安全的。
答案 5 :(得分:0)
在不将objs中存储的类型更改为可以复制并管理Obj *本身的类型的情况下,如果抛出异常,则需要添加try / catch块来清理objs。最简单的方法是:
vector<Obj *> objs;
try {...}
catch (...)
{
// delete all objs here
throw;
}
虽然如果没有抛出异常,你仍然想要清理objs。
答案 6 :(得分:0)
嗯。我真的很喜欢海军准将的想法;它整齐地清除了这段代码给我的一些有趣的气味。此时我不愿意引入Boost库;这是一个有点保守的代码库,我宁愿把它带入21世纪的蠕动和抱怨,而不是踢和尖叫。