1

だから私はこのライブラリコードを持っています、見てください...

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へのポインターを含める必要があります。また、例外を処理する必要があります。これは、例外を認識しない古いコードベースから呼び出されているためです。

私が見ているように、私のオプションは次のとおりです。

  1. クライアントコードを巨大なtryブロックでラップし、catchブロックのベクトルをクリーンアップします。
  2. すべての割り当ての周りにtryブロックを配置し、そのcatchブロックは共通のクリーンアップ関数を呼び出します。
  3. 私がまだ考えていないいくつかの巧妙なRAIIベースのアイデア。
  4. パント。現実的には、コンストラクターがスローすると、とにかくアプリケーションが炎上しそうになりますが、これをより適切に処理したいと思います。
4

7 に答える 7

5

を見てみましょうboost::ptr_vector

于 2009-02-27T22:54:19.960 に答える
4

回答 3 - ベクトルで Obj* の代わりにスマート ポインターを使用します。boost::shared_ptrをお勧めします。

于 2009-02-27T22:51:38.673 に答える
4

Thing デストラクタは既にベクトルをクリーンアップする方法を知っているので、RAIIソリューションへの道はほぼ終わりです。Objs のベクトルを作成して Thing のコンストラクターに渡す代わりに、Thing を空のベクトルで初期化し、メンバー関数を追加して、ポインタによって新しい Objs をベクトルに追加することができます。

このように、Obj のコンストラクターがスローした場合、コンパイラは Thing のデストラクタを自動的に呼び出し、既に割り当てられている Obj を適切に破棄します。

Thing のコンストラクターは no-op になります。

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 コンテナーでの使用には適していません。shared_ptrBoost によって提供される と、今後の C++0x で問題ありません。

于 2009-02-27T22:52:40.510 に答える
1

オブジェクトのベクトルは、サイズ変更時にオブジェクトを移動する必要があり、それらすべてをコピーする必要があるため、パフォーマンスが非常に低下する可能性があります。オブジェクトへのポインタの方が優れています。

私はあなたが必要とすることをする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
于 2009-02-27T22:54:11.413 に答える
0

うーん。私はイェーガー提督のアイデアが本当に好きです。このコードが私に与えていた変なにおいのいくつかをきれいに片付けます。この時点で Boost ライブラリを導入するのは気が進まない。これはやや保守的なコードベースであり、21 世紀に持ち込んで、蹴ったり叫んだりするよりも、身をよじって不平を言いたいと思います。

于 2009-02-28T04:03:19.377 に答える
0

Obj へのポインターのベクトルの代わりに、常に Obj のベクトルを使用できます。その場合、Obj を安全にコピーできることを確認する必要があります (ポインターが含まれていると危険です)。ただし、 Obj には固定サイズの char 配列のみが含まれているため、安全なはずです。

于 2009-02-27T22:50:04.463 に答える