Это должно хорошо наследовать реализацию от контейнеров STL, а не делегировать?

У меня есть класс, который адаптирует станд.:: вектор для моделирования контейнера проблемно-ориентированных объектов. Я хочу представить большую часть станд.:: векторный API пользователю, так, чтобы он мог использовать знакомые методы (размер, ясный, в, и т.д....) и стандартные алгоритмы на контейнере. Это, кажется, повторяющийся шаблон для меня в моих проектах:

class MyContainer : public std::vector<MyObject>
{
public:
   // Redeclare all container traits: value_type, iterator, etc...

   // Domain-specific constructors
   // (more useful to the user than std::vector ones...)

   // Add a few domain-specific helper methods...

   // Perhaps modify or hide a few methods (domain-related)
};

Я знаю о практике предпочтения состава к наследованию при многократном использовании класса для реализации - но существует, должен быть предел! Если я должен был делегировать все к станд.:: вектор, было бы (моим количеством) 32 передающими функциями!

Таким образом, мои вопросы... Это действительно настолько плохо для наследования реализации в таких случаях? Каковы риски? Существует ли более безопасный способ, которым я могу реализовать это без такого ввода? Действительно ли я - еретик для использования наследования реализации?:)

Править:

Что относительно того, чтобы прояснить, что пользователь не должен использовать MyContainer через станд.:: вектор <> указатель:

// non_api_header_file.h
namespace detail
{
   typedef std::vector<MyObject> MyObjectBase;
}

// api_header_file.h
class MyContainer : public detail::MyObjectBase
{
   // ...
};

Библиотеки повышения, кажется, делают этот материал все время.

Редактирование 2:

Одно из предложений должно было использовать бесплатные функции. Я покажу его здесь как псевдокод:

typedef std::vector<MyObject> MyCollection;
void specialCollectionInitializer(MyCollection& c, arguments...);
result specialCollectionFunction(const MyCollection& c);
etc...

Больше OO способ сделать его:

typedef std::vector<MyObject> MyCollection;
class MyCollectionWrapper
{
public:
   // Constructor
   MyCollectionWrapper(arguments...) {construct coll_}

   // Access collection directly
   MyCollection& collection() {return coll_;} 
   const MyCollection& collection() const {return coll_;}

   // Special domain-related methods
   result mySpecialMethod(arguments...);

private:
   MyCollection coll_;
   // Other domain-specific member variables used
   // in conjunction with the collection.
}
73
задан Emile Cormier 9 January 2010 в 22:01
поделиться

7 ответов

Кнопка ввода file чрезвычайно сложна для стиля и управления, главным образом из соображений безопасности.

Если вам действительно нужно настроить кнопку загрузки, я рекомендую загрузчик на основе Flash, такой как SWFUpload или Uploadify , который позволяет использовать пользовательское изображение в качестве кнопки, отображать индикатор выполнения и т.д.

Однако его основная философия отличается от просто встраивания элемента управления в форму. Процесс загрузки выполняется отдельно. Убедитесь, что сначала проверьте, работает ли это для вас.

-121--1511234-

html5doctor.com Обладает большой информацией о HTML5, великих разработчиках, которые пишут для него тоже. Таких, как Брюс Лоусон (Опера Дев) Реми Шарп.

-121--1844290-

Угроза освобождается с помощью указателя на базовый класс ( delete , delete [] и потенциально другие методы освобождения). Поскольку эти классы ( deque , map , string и т.д.) не имеют виртуальных dtors, их невозможно очистить должным образом, используя только указатель на эти классы:

struct BadExample : vector<int> {};
int main() {
  vector<int>* p = new BadExample();
  delete p; // this is Undefined Behavior
  return 0;
}

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

Вместо наследования или композиции, рассмотрите возможность записи свободных функций , которые принимают либо пару итераторов, либо ссылку на контейнер и работают над этим. Практически весь < алгоритм > является примером этого; и make _ heap , pop _ heap и push _ heap , в частности, являются примером использования свободных функций вместо специфичного для домена контейнера.

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

typedef std::deque<int, MyAllocator> Example;
// ...
Example c (42);
example_algorithm(c);
example_algorithm2(c.begin() + 5, c.end() - 5);
Example::iterator i; // nested types are especially easier

Обратите внимание, что value_type и распределитель могут изменяться без влияния на более поздний код с помощью typedef, и даже контейнер может изменяться с deque на вектор .

73
ответ дан 24 November 2019 в 12:20
поделиться

Проще сделать:

typedef std::vector<MyObject> MyContainer;
1
ответ дан 24 November 2019 в 12:20
поделиться

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

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

5
ответ дан 24 November 2019 в 12:20
поделиться

Вы можете комбинировать частное наследование и ключевое слово using, чтобы обойти большинство проблем, упомянутых выше: Частное наследование - это реализовано в терминах, и поскольку оно является частным вы не можете удерживать указатель на базовый класс

#include <string>
#include <iostream>

class MyString : private std::string
{
public:
    MyString(std::string s) : std::string(s) {}
    using std::string::size;
    std::string fooMe(){ return std::string("Foo: ") + *this; }
};

int main()
{
    MyString s("Hi");
    std::cout << "MyString.size(): " << s.size() << std::endl;
    std::cout << "MyString.fooMe(): " << s.fooMe() << std::endl;
}
31
ответ дан 24 November 2019 в 12:20
поделиться

Методы пересылки будут включены, в любом случае. Вы не получите лучшую производительность таким образом. На самом деле, вы, вероятно, получите худшую производительность.

1
ответ дан 24 November 2019 в 12:20
поделиться

Это статья под названием Gnome Panel Applets in Python от 2008 года на форумах Ubuntu, которая может помочь в этом.

Сюда входит определение компонента bonobo. В этой документации Gnome по разработке апплетов указано, что

Технически апплеты являются элементами управления Bonobo, встроенными в панель Gnome

. Таким образом, это правильный способ.

-121--4013175-

Altough window.onload будет соответствовать вашим потребностям, я бы рекомендовал немного ускорить процесс:

$("img.load").load(function(){
    alert($(this).width());
});

Теперь вы можете обрабатывать изображение индивидуально так же быстро, как оно загружено и не дожидаясь полного набора элементов.

-121--1507708-

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

3
ответ дан 24 November 2019 в 12:20
поделиться

SOLR не поддерживает обновление индивидуальных областей , но есть проблема Emira в этом (почти 3 года по настоящему письму).

До этого не будет реализован, вы должны обновить весь документ.

Обновление : в качестве Solr 4+ это реализовано, вот документация .

-121--2239055-

Как и все уже заявляемые, контейнеры STL не имеют виртуальных деструкторов, поэтому наследование от них небезопасно. Я всегда считал универсальным программированием с шаблонами как другой стиль OO - один без наследства. Алгоритмы определяют интерфейс, который им требуется. Это как можно ближе к , набрав уток , как вы можете попасть на статический язык.

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

template <typename Container>
class readonly_container_facade {
public:
    typedef typename Container::size_type size_type;
    typedef typename Container::const_iterator const_iterator;

    virtual ~readonly_container_facade() {}
    inline bool empty() const { return container.empty(); }
    inline const_iterator begin() const { return container.begin(); }
    inline const_iterator end() const { return container.end(); }
    inline size_type size() const { return container.size(); }
protected: // hide to force inherited usage only
    readonly_container_facade() {}
protected: // hide assignment by default
    readonly_container_facade(readonly_container_facade const& other):
        : container(other.container) {}
    readonly_container_facade& operator=(readonly_container_facade& other) {
        container = other.container;
        return *this;
    }
protected:
    Container container;
};

template <typename Container>
class writable_container_facade: public readable_container_facade<Container> {
public:
    typedef typename Container::iterator iterator;
    writable_container_facade(writable_container_facade& other)
        readonly_container_facade(other) {}
    virtual ~writable_container_facade() {}
    inline iterator begin() { return container.begin(); }
    inline iterator end() { return container.end(); }
    writable_container_facade& operator=(writable_container_facade& other) {
        readable_container_facade<Container>::operator=(other);
        return *this;
    }
};

Эти классы подвергают тот же интерфейс, что и контейнер STL. Мне понравилось влияние отделения модифицирующих и не изменяющих операций на различные базовые классы. Это действительно приятно влияет на корректность Const. Единственный недостаток в том, что вы должны продлить интерфейс, если вы хотите использовать их с ассоциативными контейнерами. Я не столкнулся с необходимостью.

14
ответ дан 24 November 2019 в 12:20
поделиться
Другие вопросы по тегам:

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