2013-05-01 3 views
2

У меня есть класс SpecialArray, к которому можно получить доступ так же, как и стандартный двумерный массив; то есть как A[i][j]. Часто бывает необходимо извлечь одномерный массив из int с этого объекта. Детали извлечения, зависящие от других переданных параметров, несущественны; Я заинтересован в разработке интерфейса операции.Каковы преимущества и недостатки использования классов, которые отличаются только конструкторами?

Каковы преимущества и недостатки следующих подходов к решению этой задачи?

Вариант 1

Определим функцию:

std::vector<int> extract(const SpecialArray &A, ...) 

где здесь ... относится к другим параметрам, которые определяют содержание одномерного массива, а также использовать его в качестве:

std::vector<int> output = extract(A,...); 

Вариант 2

Мы создаем класс, который наследуется от станд :: вектор и строит себя:

class SpecialArrayExtract : public std::vector<int> { 
    public: 
     SpecialArrayExtract(const SpecialArray &A, ...); 
}; 

где конструктор (или, что то же самое, init функция) использует ... вход для заполнения *this с соответствующими данными, и мы используем его как:

SpecialArrayExtract output(A,...); 

Вариант 3

Переходим точно так, как в варианте 2, но не наследуют от std::vector<int> и вместо того, чтобы иметь private элемент std::vector<int> с интерфейсом подвергаются при необходимости:

class SpecialArrayExtract { 
    private: 
     std::vector<int> m_data; 
    public: 
     SpecialArrayExtract(const SpecialArray &A, ...); 
     [Wrapper functions for std::vector<int> here.] 
}; 

Комментарий

Приложение высокоэффективная, но с RVO в Варианте 1, я предполагаю, что все они должны выполняться эквивалентно.

Закрепляя это обратно в заголовок, точка, которую я пытаюсь понять, состоит в том, что оба варианта 2 и 3 определяют классы, которые по существу равны std::vector<int> с другим именем и специальным конструктором. когда - если когда-либо! - Это разумная идея, и какой из них лучше?

+0

Итак, у вас есть структура данных, которая содержит несколько массивов, и вам необходимо предоставить доступ ко всем массивам? Ваш метод извлечения копирует из внутреннего хранимого массива в целевой массив (независимо от того, является ли RVO или семантикой перемещения); действительно ли нужно его скопировать? – dyp

+3

Вариант 2 очень плохой, вы не должны наследовать публично из std :: vector, так как он не имеет виртуального деструктора ... – JBL

+0

@DyP Выделенный массив представляет собой последовательность элементов из исходного многомерного массива, которые не являются [вероятными] рядом с памятью. (То есть, я не просто вытаскиваю столбец или строку матрицы здесь.) Мне нужно будет циклически перебирать извлеченный массив, и мне нужно, чтобы эти элементы были смежными в памяти для оптимального использования кеша. – Ben

ответ

3

Я считаю, что вариант один имеет больше смысла, потому что:

  • имеет тривиальный простой интерфейс
  • не создает дополнительные классы
  • может быть расширен с помощью шаблонов для других «добычи» операций

Я против второго варианта, потому что если вы унаследовали от std::vector<int> вы говорите, что SpecialArrayExtractесть-std::vector<int> и обеспечивает одну операцию (конструктор), чтобы создать его. Если вы думаете об этом, это всего лишь сложный способ сделать то же самое, что и ваша первая альтернатива.

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

Что касается опции 4 (предлагается в ответе), я считаю, что интерфейс класса должен содержать как можно меньше экземпляров экземпляра, а вместо этого предлагать не-членные функции (см. this article, чтобы узнать обоснование).

Если ваши потребности должны были измениться в будущем (например, вы решили, что другой контейнер или тип данных лучше обслуживают ваши потребности), вы можете изменить функцию с помощью шаблонов. Это даст вам все преимущества наличия отдельного класса, но не накладные расходы. И та же самая операция (с точно таким же именем) можно применить к любой возможной комбинации ContainerTypes (т.е. std::vector<int> или std::array<int, 12>, или любой другой) и ArrayTypes (SpecialArray или SparseSpecialArray, или любой другой):

template<typename ContainerType, typename ArrayType> 
ContainerType extract(const ArrayType& array, ...) 

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

1

Вариант 4

Реализовать функцию общественного члена для класса SpecialArray:

std::vector<int> extract(...); 
0

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

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

2
template < typename OutIt > 
void extract(SpecialArray const&, OutIt dest_begin); 

Это работает в любом контейнере для любого предопределения контейнера, в котором хранится результат.

Если вы последовательно пишете элементы (à la push_back), вы можете использовать back_insert_iterator, чтобы изменить размер контейнера, если это необходимо, но это изменит размер, например. вектор, если он не является достаточно большим, что приводит к некоторому влиянию на производительность. В противном случае вы можете использовать Итератор с произвольным доступом (не меняет подпись шаблона функции, возможно, меняет имя от OutIt до RAIt).

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

std::size_t extract_size() const; 

Пример:

SpecialArray my_special; 

constexpr std::size_t len = 100; 
int     dest_ra[len]; 
std::array<int, len> dest_a; 
std::vector<int>  dest_v; 
std::list<int>  dest_l; 

if(len >= my_special.extract_length()) 
{ 
    extract(my_special, std::begin(dest_ra)); 
    extract(my_special, std::begin(dest_a)); 
} 

// using `push_back`: 
    dest_v.reserve(my_special.extract_length());  // not necessary 
    extract(my_special, std::back_inserter(dest_v)); 

    extract(my_special, std::back_inserter(dest_l)); 

// if random access is required, also a bit faster(*): 
    dest_v.resize(my_special.extract_length()); 
    extract(my_special, std::begin(dest_v)); 

    // not possible for the list 

(*) back_insert_iterator должен делать проверку диапазона для того, чтобы увеличить вектор. Если вы используете обычный итератор, проверок диапазонов нет.


Мне нравится аргументация Арриеты и соглашайтесь с решением вариантов.

Смежные вопросы