سؤال

هل هناك أي طريقة غير فظيعة لديك مجموعة من الكائنات من أكثر من نوع واحد?أنا سعيد تماما لاشتقاق كل نوع من قاعدة مشتركة.أحتاج إلى دلالات معقولة حتى يمكن نسخ المجموعة وتعيينها وما إلى ذلك.

من الواضح ، لا يمكنني فقط استخدام متجه أو قائمة من الفئة الأساسية.سيتم تقطيع الكائنات ولن يعمل النسخ على الإطلاق.يعمل استخدام المتجهات أو قوائم المؤشرات أو المؤشرات الذكية ، لكنك لا تحصل على دلالات نسخ عاقلة.

للحصول على دلالات نسخة عاقل ، تحتاج إلى استخدام شيء مثل دفعة ptr_vector.ولكن هذا يتطلب بنية تحتية مؤلمة وعرضة للخطأ.بشكل أساسي ، لا يمكنك فقط اشتقاق فئة جديدة من الفئة الأساسية لأنه إذا دخلت المجموعة ، فلن يتم نسخها بشكل صحيح.

يبدو أن هذا شيء شائع يجب القيام به وجميع الحلول التي أعرفها فظيعة للغاية.يبدو أن ج missing يفتقد بشكل أساسي طريقة لإنشاء مثيل جديد لكائن مطابق لمثيل معين-حتى إذا كان هذا النوع يحتوي على منشئ نسخ.وجعل clone أو duplicate تتطلب الوظيفة تحميلا زائدا دقيقا في كل فئة مشتقة.إذا فشلت في القيام بذلك عند إنشاء فئة جديدة مشتقة من القاعدة (أو أي فئة أخرى مشتقة من تلك القاعدة) - ازدهار ، فواصل مجموعتك.

هل هناك حقا طريقة أفضل?

هل كانت مفيدة؟

المحلول

يمكنك استخدام std::vector<boost::any> للقيام بمعظم هذا على ما أعتقد.

#include "boost/any.hpp"
#include <vector>
#include <iostream>

//Simple class so we can see what's going on
class MM {
  public:
    MM()               { std::cout<<"Create @ "<<this<<std::endl; }
    MM( const MM & o ) { std::cout<<"Copy "<<&o << " -> "<<this<<std::endl; }
    ~MM()              { std::cout<<"Destroy @ "<<this<<std::endl; }
};

int main()
{
  //Fill a vector with some stuff
  std::vector<boost::any> v;
  v.push_back(0);
  v.push_back(0);
  v.push_back(0);
  v.push_back(0);

  //Overwrite one entry with one of our objects.
  v[0] = MM();

  std::cout<<"Copying the vector"<<std::endl;
  std::vector<boost::any> w;
  w = v;

  std::cout<<"Done"<<std::endl;
}

التي أحصل على الإخراج:

Create @ 0xbffff6ae
Copy 0xbffff6ae -> 0x100154
Destroy @ 0xbffff6ae
Copying the vector
Copy 0x100154 -> 0x100194
Done
Destroy @ 0x100194
Destroy @ 0x100154

وهو ما أتوقع رؤيته.

تحرير:

تمشيا مع الاحتياجات الخاصة بك لتكون قادرة على التعامل مع أعضاء وبعض قاعدة مشتركة من نوع ستحتاج شيئا مشابها جدا ل boost::any, ، وهو لحسن الحظ فئة بسيطة نسبيا.

template<typename BASE>
class any_with_base
{
    // ... Members as for boost::any

    class placeholder
    {
        virtual BASE * as_base() = 0;

        //Other members as in boost::any::placeholder
    };

    template<typename ValueType>
    class holder : public placeholder
    {
        virtual BASE * as_base() { return (BASE*)&held; }
        //Other members as in boost::any::holder<T>
    };

    BASE* as_base() { return content?content->as_base():0; }
}

الآن يجب أن تكون قادرا على القيام بذلك:

vector< any_with_base<Base> > v;
v.push_back( DerivedA() );
v.push_back( DerivedB() );

v[0].as_base()->base_fn();
v[1].as_base()->base_fn();

any_cast<DerivedA>(v[0])->only_in_a();

أنا في الواقع لا أحب بناء جملة أي بث وسأغتنم هذه الفرصة لإضافة وظيفة عضو "كـ"..حتى أتمكن من كتابة السطر الأخير على النحو التالي:

v[0].as<DerivedA>()->only_in_a();

نصائح أخرى

حسنا ، لمتابعة تعليقي ، هناك طريقة للقيام بذلك دون استخدام دفعة::أي التي ينبغي أن تقدم الأداء المتفوق في معظم الحالات ، ولكن من المسلم به أكثر قليلا المشاركة.بلدي الحل يجمع بين فكرتين:استخدام فئة حامل أن يلغي نوع محتوياته ، وخفيفة الوزن رتتي مخصص.نعطي فئة حامل نسخة ذات مغزى ودلالات التعيين ونحن نستخدم حاويات من أصحاب لإدارة مجموعة من الكائنات.نحن نستخدم لدينا رتتي خفيفة الوزن لاكتشاف أنواع حقيقية من الكائنات عند الضرورة.إليك بعض التعليمات البرمجية لإظهار ما أقترحه:

#include <vector>
#include <cassert>
#include <iostream>

#include <boost/cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/static_assert.hpp>

/// This template makes it possible to enforce the invariant that every type in a
/// hierarchy defines the id( ) function, which is necessary for our RTTI.  Using
/// a static assertion, we can force a compile error if a type doesn't provide id( ).

template< typename T >
struct provides_id {
  typedef char one;
  typedef long two;

  template< typename U, std::string const &(U::*)( ) const = &U::id >
  struct id_detector { };

  template< typename U > static one test( id_detector< U > * );
  template< typename U > static two test( ... );

  enum { value = sizeof(test<T>(0)) == sizeof(one) };
};

/// Base class for the holder.  It elides the true type of the object that it holds,
/// providing access only through the base class interface.  Since there is only one
/// derived type, there is no risk of forgetting to define the clone() function.

template< typename T >
struct holder_impl_base {
  virtual ~holder_impl_base( ) { }

  virtual T       *as_base( )       = 0;
  virtual T const *as_base( ) const = 0;

  virtual holder_impl_base *clone( ) const = 0;
};

/// The one and only implementation of the holder_impl_base interface.  It stores
/// a derived type instance and provides access to it through the base class interface.
/// Note the use of static assert to force the derived type to define the id( )
/// function that we use to recover the instance's true type.

template< typename T, typename U >
struct holder_impl : public holder_impl_base< T > {
  BOOST_STATIC_ASSERT(( provides_id< U >::value ));

  holder_impl( U const &p_data )
    : m_data( p_data )
  { }

  virtual holder_impl *clone( ) const {
    return new holder_impl( *this );
  }

  virtual T *as_base( ) {
    return &m_data;
  }

  virtual T const *as_base( ) const {
    return &m_data;
  }

private:

  U m_data;
};

/// The holder that we actually use in our code.  It can be constructed from an instance
/// of any type that derives from T and it uses a holder_impl to elide the type of the
/// instance.  It provides meaningful copy and assignment semantics that we are looking
/// for.

template< typename T >
struct holder {

  template< typename U >
  holder( U const &p_data )
    : m_impl( new holder_impl< T, U >( p_data ))
  { }

  holder( holder const &p_other )
    : m_impl( p_other.m_impl -> clone( ))
  { }

  template< typename U >
  holder &operator = ( U const &p_data ) {
    m_impl.reset( new holder_impl< T, U >( p_data ));
    return *this;
  }

  holder &operator = ( holder const &p_other ) {
    if( this != &p_other ) {
      m_impl.reset( p_other.m_impl -> clone( ));
    }

    return *this;
  }

  T *as_base( ) {
    return m_impl -> as_base( );
  }

  T const *as_base( ) const {
    return m_impl -> as_base( );
  }

  /// The next two functions are what we use to cast elements to their "true" types.
  /// They use our custom RTTI (which is guaranteed to be defined due to our static
  /// assertion) to check if the "true" type of the object in a holder is the same as
  /// as the template argument.  If so, they return a pointer to the object; otherwise
  /// they return NULL.

  template< typename U >
  U *as( ) {
    T *base = as_base( );

    if( base -> id( ) == U::static_id( )) {
      return boost::polymorphic_downcast< U * >( base );
    }

    return 0;
  }

  template< typename U >
  U const *as( ) const {
    T *base = as_base( );

    if( base -> id( ) == U::static_id( )) {
      return boost::polymorphic_downcast< U const * >( base );
    }

    return 0;
  }

private:

  boost::scoped_ptr< holder_impl_base< T > > m_impl;
};

/// A base type and a couple derived types to demonstrate the technique.

struct base {
  virtual ~base( )
  { }

  virtual std::string const &id( ) const = 0;
};

struct derived1 : public base { 
  std::string const &id( ) const {
    return c_id;
  }

  static std::string const &static_id( ) {
    return c_id;
  }

private:

  static std::string const c_id;
};

std::string const derived1::c_id( "derived1" );

struct derived2 : public base {
  std::string const &id( ) const {
    return c_id;
  }

  static std::string const &static_id( ) {
    return c_id;
  }

private:

  static std::string const c_id;
};

std::string const derived2::c_id( "derived2" );

/// A program to demonstrate that the technique works as advertised.

int main( ) {
  std::vector< holder< base > > vector1;

  vector1.push_back( derived1( ));
  vector1.push_back( derived2( ));

  std::vector< holder< base > > vector2 = vector1;

  /// We see that we have true copies!

  assert( vector1[0].as_base( ) != vector2[0].as_base( ));
  assert( vector1[1].as_base( ) != vector2[0].as_base( ));

  /// Easy assignment of container elements to new instances!

  vector2[0] = derived2( );
  vector2[1] = derived1( );

  // Recovery of the "true" types!

  std::vector< holder< base > >::iterator l_itr = vector1.begin( );
  std::vector< holder< base > >::iterator l_end = vector1.end  ( );

  for( ; l_itr != l_end; ++l_itr ) {
    if( derived1 *ptr = l_itr -> as< derived1 >( )) {
      std::cout << ptr -> static_id( ) << std::endl;
    }
    else if( derived2 *ptr = l_itr -> as< derived2 >( )) {
      std::cout << ptr -> static_id( ) << std::endl;
    }
  }
}

وهنا الإخراج:

derived1
derived2
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top