создание подклассов с или без виртуалов против производительности против удобства - PullRequest
2 голосов
/ 13 июня 2011

Хорошо известно, что классы STL нигде не используют виртуальные методы (и STL также нигде не использует наследование, и что эти два факта взаимосвязаны), и что STL не уникален в этом.

Давайте предположим, что на земле существуют другие фанаты производительности (ну, они существуют), такие фанаты производительности, которые задают себе вопрос для каждого класса: «Нужны ли мне виртуальные методы для этого класса X?»и «может ли этот класс X обойтись без каких-либо виртуалов, как классы STL, для лучшей производительности?»

Отсутствие каких-либо виртуальных методов (включая d'tor) делает полиморфизм и создание подклассов более сложным, чем с базой «virtuals»классы.Очевидно, что «не виртуальные» классы не хорошо подходят для базовых классов.

Вопрос: существует ли методика (для c ++), позволяющая программисту создать за один раз две версии одного и того же класса X, "не виртуальную" версию Xnv (для производительности) и«виртуальные» версии Xv, для подклассов?Если это не нужно, объясните, почему.

Post-note

Люди ответили "Если вам нужно создать подклассы, используйте виртуальные. Если нет, не используйте виртуальные«.

Существует проблема с этим предложением.Пара проблем.

1) нуждается в изменениях подстилки со временем.подклассы из класса X тогда не были нужны, но нужны сейчас, или наоборот.
2) Человек, который пишет базовый класс, не тот, кто пишет производный класс.Это ясно из вопроса.У людей разные стили мышления, разные суждения, разные потребности.Понятно, снова.
3) Следовательно, разные программисты, отвечая на вопрос типа «имеет ли смысл наследование от класса X?», Будут давать разные ответы.Это субъективно, точного ответа нет.
4) Это противоречит тому, что задает вопрос.

Следовательно, мы хотим удовлетворить два конца спектра - что часто случается в технике - и это мотивация вопроса.

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

Никто не понял мотивации - к моему удивлению - возможно, даже сейчас.Это будет уроком для меня.

Я принял ответ, в котором упоминается CRTP, потому что это веселый паттерн.

Ответы [ 6 ]

1 голос
/ 13 июня 2011

Вместо того, чтобы беспокоиться о производительности в первую очередь, лучше задать вопрос: "Имеет ли смысл наследование от этого класса?" Если ответ «нет», зачем делать что-то виртуальное? Есть, наконец, некоторые преимущества хранения и производительности по сравнению с тем, что по сути является финальным классом. (Хотя C ++ не поддерживает концепцию Java класса final, класс без виртуальных методов довольно близок к тому, чтобы быть «конечным».)

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

1 голос
/ 13 июня 2011

Вопрос: существует ли методика (для c ++), позволяющая программисту за один раз создать две версии одного и того же класса X, «не виртуальную» версию Xnv (для производительности) и «виртуальную» версию Xv,для подклассов?

Зачем это делать, когда вы можете получить свой торт и съесть его тоже?

С CRTP вы получаете полиморфизм во время компиляции и возможность для подклассов переопределятьповедение, без каких-либо накладных расходов для виртуальных функций.

В качестве альтернативы, вы можете использовать класс «черты» для внедрения поведения.

1 голос
/ 13 июня 2011

Полагаю, вы могли бы ...

struct Base
{
    virtual ~Base();
    virtual void foo();
};

struct Dummy {};

template <bool is_virtual>
struct SelectBase
{
    typedef Base type;
};

template <>
struct SelectBase<false>
{
    typedef Dummy type;
};

template <bool is_virtual>
struct MyClass : SelectBase<is_virtual>::type
{
    ~MyClass();
    void foo();
};

int main()
{
    Base* xv = new MyClass<true>(); // virtual version
    MyClass<false>* xnv = new MyClass<false>(); // non-virtual version
    xv->foo(); // virtual call
    xnv->foo(); // non-virtual call
}

Хотя я не могу придумать вескую причину сделать это.

0 голосов
/ 13 июня 2011

Я не согласен с этим предположением:

точно так же, как классы STL, для лучшей производительности?

Я не согласен с тем, что недостаток виртуального является причиной производительности.

Отсутствие каких-либо виртуальных методов (включая d'tor) затрудняет полиморфизм и создание подклассов по сравнению с базовыми классами "virtuals".

Совершенно верно.И это было бы моей причиной отсутствия виртуальных функций в классах контейнера.Он специально разработан, чтобы не быть подклассом.Лучше использовать членство, а не наследование при использовании контейнерных классов.

Вопрос: существует ли методика (для c ++), позволяющая программисту создать за один раз две версии одного и того же класса X, a "«не виртуальные» версии Xnv (для производительности) и «виртуальные» версии Xv для подклассов?Если это не нужно, объясните, почему.

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

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

0 голосов
/ 13 июня 2011

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

template<typename T> class something {
    virtual const T& operator*() = 0;
    virtual something<T>& operator++() = 0;  // This should be only prefix
    // etc
};

template<typename Derived, typename T> class something_better : public something<T> {
    typename Derived::const_iterator i;
public:
    something_better(const Derived& d) {
        i = d.begin();
    }
    const T& operator*() {
        return *i;
    }
    something<T>& operator++() {
        ++i;
        return *this;
    }
    something<T>& operator--() {
        --i;
        return *this;
    }
};

Et вуаля - полиморфная итерация во время выполнения.Это было просто.Конечно, до тех пор, пока итератор и контейнер остаются действительными.

Если вы хотите неконстантную итерацию и другие вещи, вам придется ждать, пока C ++ 0x правильно обработает r-значения,Или спросите BOOST_FOREACH людей, кажется, что они как-то решили проблему.

0 голосов
/ 13 июня 2011

Я думаю, что вы не уверены в причинах, которые определяют решение сделать метод виртуальным или нет. Производительность - , а не - одна из причин, по которой ваши методы будут виртуальными или нет, и, как правило, чаще всего эффект производительности будет минимальным [1] .

Решение о предоставлении виртуальных методов должно основываться на ваших требованиях, в частности: нужен ли вам полиморфизм во время выполнения? Если вы это сделаете, то методы должны быть виртуальными, а если нет, то не должны.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...