когда лучше использовать шаблон с ++? - PullRequest
3 голосов
/ 31 августа 2009

сейчас я изучаю C ++, и теперь я знаю основную концепцию шаблона, которые действуют как родовой тип, и я нашел почти в каждой программе на С ++ используемый шаблон, Так что я действительно хочу знать, когда мы должны использовать шаблон? Может кто-нибудь заключить ваш опыт для меня о шаблоне c ++? Когда вы рассмотрите возможность использования шаблона?

Дополнение: если бы мы определили такую ​​функцию

template <class myType>
myType GetMax (myType a, myType b) {
 return (a>b?a:b);
}

но мы хотим передать объект (самоопределяемый класс) для сравнения, как мы можем реализовать?

Supplement2: в ответе ниже кто-то написал этот пример кода

template <class myType>
const myType& GetMax (const myType& a, const myType& b) {
    return (a<b?b:a);
}

template <class myType, class Compare>
const myType& GetMax (const myType& a, const myType& b, Compare compare) {
    return (compare(a,b)?b:a);
}

это правильно? мы можем просто передать имя функции в качестве параметра класса myType?

Ответы [ 10 ]

8 голосов
/ 31 августа 2009

G'day,

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

Таким образом, стек целых чисел будет вести себя так же, как стек чисел с плавающей точкой будет вести себя как стек объектов MyClass.

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

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

Затем он сообщает любому, кто создает экземпляр производного класса, например, класса Lion, что он должен предоставить реализацию функции-члена makeSound, которая каким-то образом обеспечит рев.

Редактировать: Я забыл добавить, что это одна из статей в превосходной книге Скотта Мейерса "Эффективный C ++" ( очищенная ссылка Amazon ), которую я настоятельно рекомендую.

НТН

ура

3 голосов
/ 31 августа 2009

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

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

//----- the container
template <class T>
class Stack
{
public:
    T* stackPtr;
}

//----- example
void main()
{
    typedef Stack<float> FloatStack;
    typedef Stack<int> IntStack;
}

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

2 голосов
/ 31 августа 2009

В приведенном вами примере все в порядке, если operator > определено для типа данных, экземпляры которого вы сравниваете.

Например, если вы определите следующий класс:

class fraction
{
private:
    int _num, _den;

public:
    fraction(int num, int den)
    {
        if (den >= 0)
        {
            _num = num;
            _den = den;
        }
        else
        {
            _num = -num;
            _den = -den;
        }
    }

    fraction(const fraction &f)
    {
        _num = f._num;
        _den = f._den;
    }

    bool operator > (const fraction &f) const
    {
        return (_num * f._den) > (f._num * _den);
    }

    bool operator == (const fraction &f) const
    {
        return (_num * f._den) == (f._num * _den);
    }
};

Затем вы можете использовать функцию шаблона с экземплярами этого класса.

int main(int argc, char* argv[])
{
    fraction a(1,2); // 0.5
    fraction b(3,4); // 0.75
    assert(GetMax/*<fraction>*/(a,b) == a);
    return 0;
}
2 голосов
/ 31 августа 2009

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

До STL / boost они неплохо делали контейнеры.

1 голос
/ 31 августа 2009

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

template <class myType>
const myType& GetMax (const myType& a, const myType& b) {
    return (a<b?b:a);
}

template <class myType, class Compare>
const myType& GetMax (const myType& a, const myType& b, Compare compare) {
    return (compare(a,b)?b:a);
}

Использование Samle: для сравнения строк в стиле C:

bool c_strings_less(const char* a, const char* b)
{
    return std::strcmp(a, b) < 0; //is a less than b
}

const char* greater = GetMax("hello", "world", c_strings_less);

Вот как работает алгоритм std :: max. (Я также сделал несколько модификаций, например, в C ++ обычно предикаты определяют сравнение «меньше».)

Или, если вы спросите, как GetMax будет работать для произвольных пользовательских типов, тогда они должны перегрузить оператор>, иначе ваша функция приведет к ошибке компиляции.

1 голос
/ 31 августа 2009

Отвечая на второй вопрос ( но мы хотим передать объект (самоопределяемый класс) для сравнения, как мы можем реализовать?)

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

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

/ Tobias

1 голос
/ 31 августа 2009

Когда вам нужно параметризовать концепцию, представленную классом.

Например, если у вас есть класс, представляющий способ управления типом объекта

class MyThingManager
{
  void add( MyThing& mything ); 
  //...
};

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

template< class ThingType >
 class ThingManager
    {
      void add( ThingType& thing ); 
      //...
    };

Таким образом, вы не дублируете код.

Другая проблема заключается в том, что вы хотите, чтобы какой-то вызов функции был совместим с любым параметром, который имеет требуемую семантику:

template< class MyType >
void addPi( MyType& value )
{
    value += PI;
}

Таким образом, вам (опять же) не нужно дублировать код для каждого возможного типа в параметрах.

Это не называется "общим программированием" даром.

Это простые случаи, но существуют более сложные случаи, когда вы хотите заняться метапрограммированием. Если вы хотите пойти туда, пожалуйста, прочитайте хотя бы одну книгу, прежде чем писать адский код. Для этого я рекомендую «Мета-программирование шаблонов C ++» и отличную книгу «Современный дизайн C ++» для более сложного использования шаблонов, таких как шаблон политики и другие хорошо известные.

0 голосов
/ 31 августа 2009

Важно отметить, что совершенно нормально не писать свои собственные шаблоны. Если вы не уверены, зачем они вам нужны, тогда они вам, вероятно, не нужны. Шаблоны - очень мощный инструмент, но они не всегда являются лучшим решением.

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

0 голосов
/ 31 августа 2009

Шаблон обеспечивает способ параметризации на ИЗВЕСТНЫХ В СКОРОСТИ ВРЕМЕНИ. Обратите внимание, что это может быть тип (std :: vector будет содержать только целые числа), но они также могут быть значениями:

template <int N, typename T > class MyType

шаблонизируется как для целого числа, так и для типа, и MyType <2, int> будет отличаться от MyType <3, int>.

Кроме того, шаблоны допускают метапрограммирование шаблонов: компилятор выполняет программу во время компиляции. У Эрвина Унру есть замечательный пример вычисления простых чисел во время компиляции.

Посмотрите на http://ubiety.uwaterloo.ca/~tveldhui/papers/priority.html для небольшого кусочка истории.

0 голосов
/ 31 августа 2009

если вы не знаете тип вашей переменной или хотите сделать то же самое для многих типов переменных, вы можете использовать шаблон ...

если вы хотите добавить 2 int, вы хотите получить int для возврата если вы хотите добавить 2 двойных, вы хотите получить двойной для возврата

поэтому вы используете шаблон для этого ..

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