Лучшая практика для списка полиморфных объектов в C++

C #

using System;
using OpenQA.Selenium.PhantomJS;
using System.Drawing.Imaging;

namespace example.com
{
    class Program
    {
        public static PhantomJSDriver driver;

        public static void Main(string[] args)
        {
            driver = new PhantomJSDriver();
            driver.Manage().Window.Size = new System.Drawing.Size(1280, 1024);
            driver.Navigate().GoToUrl("http://www.example.com/");
            driver.GetScreenshot().SaveAsFile("screenshot.png", ImageFormat.Png);
            driver.Quit();
        }
    }
}

Требуются NuGetPackages:

  1. PhantomJS 2.0.0
  2. Selenium.Support 2.48.2
  3. Selenium.WebDriver 2.48.2

Протестировано с .NETFramework v4.5.2

5
задан KdgDev 3 July 2009 в 20:21
поделиться

4 ответа

В чем проблема std :: list (или его std :: list )?

Это был бы идиоматический способ реализации списка shape s с полиморфным поведением.

  1. I хотите, чтобы использование этой системы было безопасным; Я не хочу, чтобы пользователь имел неопределенные ошибки, когда он / она ошибочно приводил указатель базового класса к неправильному производному типу.

Пользователи не должны приводить к понижению, а скорее использовать предоставленные полиморфизм и базовые операции (формы). Подумайте, почему они будут заинтересованы в понижающем преобразовании. Если вы найдете причину для этого, вернитесь к чертежной доске и измените дизайн, чтобы ваша база обеспечивала все необходимые операции.

Затем, если пользователь хочет понижать, они должны использовать dynamic_cast , и они получат то же поведение, которое вы пытаетесь обеспечить в своей оболочке (либо нулевой указатель, если понижающие указатели, либо исключение std :: bad_cast для ссылочного понижающего приведения).

Ваш Решение добавляет уровень косвенности и (с предоставленным интерфейсом) требует, чтобы пользователь попытался угадать тип формы перед использованием. Вы предлагаете два оператора преобразования для каждого из производных классов, но пользователь должен вызвать их, прежде чем пытаться использовать методы (которые больше не являются полиморфными).

  1. Кроме того, я хочу как можно больше работы по копированию / сериализации этого списка заботиться автоматически. Причина этого в том, что по мере добавления нового производного типа я не хочу, чтобы мне приходилось искать во многих исходных файлах и убедиться, что все будет совместимо.

Не занимаясь десериализацией (я вернусь позже), ваше решение, по сравнению с хранением (интеллектуальных) указателей в списке, требует повторного посещения адаптера, чтобы добавить новый код для каждого и каждого другого класса, добавляемого в иерархию. .

Теперь проблема десериализации .

Предлагаемое решение использует простой std :: list , когда у вас есть список, отрисовка и сериализация могут быть выполнены правильно из коробки:

class shape
{
public:
   virtual void draw() = 0;
   virtual void serialize( std::ostream& s ) = 0;
};
typedef std::list< boost::shared_ptr<shape> > shape_list;
void drawall( shape_list const & l )
{
   std::for_each( l.begin(), l.end(), boost::bind( &shape::draw, _1 ));
}
void serialize( std::ostream& s, shape_list const & l )
{
   std::for_each( l.begin(), l.end(), boost::bind( &shape::serialize, _1, s ) );
}

Где я использовал boost :: bind для уменьшения раздувания кода вместо повторения вручную. Проблема в том, что вы не можете виртуализировать конструкцию, так как до того, как объект был построен, вы не можете знать, какой он тип на самом деле. После того, как проблема десериализации одного элемента известной иерархии решена, десериализация списка становится тривиальной.

Решения этой проблемы никогда не бывают такими чистыми и простыми, как приведенный выше код.

Я предполагаю, что вы определили уникальные значения типа фигуры для всех фигур, и что ваша сериализация начинается с печати этот идентификатор. То есть первым элементом сериализации является идентификатор типа.

const int CIRCLE = ...;
class circle : public shape
{
   // ...
public:
   static circle* deserialize( std::istream & );
};
shape* shape_deserialize( std::istream & input )
{
   int type;
   input >> type;
   switch ( type ) {
   case CIRCLE:
      return circle::deserialize( input );
      break;
   //...
   default:
      // manage error: unrecognized type
   };
}

Вы можете дополнительно уменьшить необходимость работы с функцией десериализатора, если вы преобразовываете ее в абстрактную фабрику, где при создании нового класса сам класс регистрирует свой метод десериализации. .

typedef shape* (*deserialization_method)( std::istream& );
typedef std::map< int, deserialization_method > deserializer_map;
class shape_deserializator
{
public:
   void register_deserializator( int shape_type, deserialization_method method );
   shape* deserialize( std::istream& );
private:
   deserializer_map deserializers_;
};

shape* shape_deserializator::deserialize( std::istream & input )
{
   int shape_type;
   input >> shape_type;
   deserializer_map::const_iterator s = deserializers_.find( shape_type );
   if ( s == deserializers_.end() ) {
      // input error: don't know how to deserialize the class
   }
   return *(s->second)( input ); // call the deserializer method
}

В реальной жизни я бы использовал boost :: function <> вместо указателей функций, что сделало бы код более чистым и понятным, но добавило бы еще одну зависимость к примеру кода.

5
ответ дан 14 December 2019 в 04:45
поделиться

You could avoid lots of repetition in GenericShape by using templates (for the constructors and converters), but the key bit that's missing is having it inherit from Shape and implement its virtuals -- without it it's unusable, with it it's a pretty normal variant on envelope/implementation idioms.

You may want to use auto_ptr (or somewhat-smarter pointers) rather than a bare pointer to Shape, too;-).

3
ответ дан 14 December 2019 в 04:45
поделиться

Я бы предложил boost :: shared_pointer в контейнере STL. Затем используйте dynamic_cast , чтобы гарантировать правильность типа с понижением. Если вы хотите предоставить вспомогательные функции для отбрасывания исключений вместо возврата NULL , следуйте предложению Алекса и определите вспомогательную функцию шаблона, например:

template <typename T, typename U>
T* downcast_to(U *inPtr) {
    T* outPtr = dynamic_cast<T*>(inPtr);
    if (outPtr == NULL) {
        throw std::bad_cast("inappropriate cast");
    }
    return outPtr;
}    

, и используйте ее так:

void some_function(Shape *shp) {
    Circle *circ = downcast_to<Circle>(shp);
    // ...
}

Использование отдельного класса, такого как GenericShape , слишком сильно связано с каждым классом, который происходит от Shape . Интересно, будет ли это считаться запахом кода или нет ...

0
ответ дан 14 December 2019 в 04:45
поделиться

Я хочу, чтобы использование этой системы было сейф; Я не хочу, чтобы у пользователя было неопределенные ошибки, когда он / она ошибочно приводит указатель базового класса к неправильному производному типу.

Почему возникают неопределенные ошибки? Поведение dynamic_cast совершенно четко определено и обнаруживает ошибку, если вы приводите указатель базового класса к неправильному производному типу. Это действительно похоже на изобретение колеса.

Кроме того, я хочу столько же возможна работа для копирование / сериализация этого списка, чтобы быть заботятся автоматически. В Причина этого в том, что новый производный тип добавлен я не хочу поиск по множеству исходных файлов и убедитесь, что все будет совместимы.

Я не уверен, в чем проблема. Если все производные классы сериализуемы и копируемы, разве этого недостаточно? Что еще вам нужно?

Я также не уверен, что делать с первыми двумя требованиями. Что вы имеете в виду, ABC должна «обеспечивать общую функциональность»? И какой смысл иметь производные классы, если их роль состоит только в том, чтобы выполнять те же самые общие функции, быть копируемыми и сериализуемыми?

Почему бы тогда просто не сделать один неабстрактный класс сериализуемым и копируемым?

Я возможно, здесь упущено что-то важное, но я не думаю, что вы объяснили, чего пытаетесь достичь.

0
ответ дан 14 December 2019 в 04:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: