11.9. Представление числового вектора фиксированного размера
11.9. Представление числового вектора фиксированного размера
Проблема
Требуется иметь эффективное представление числовых векторов фиксированного размера.
Решение
В программном обеспечении обычного типа часто более эффектный результат по сравнению с valarray дает применение специальной реализации вектора, когда его размер заранее известен на этапе компиляции. Пример 11.17 показывает, как можно реализовать шаблон вектора фиксированного размера, названный здесь kvector.
Пример 11.17. kvector.hpp
#include <algorithm>
#include <cassert>
template<class Value_T, unsigned int N>
class kvector {
public:
// открытые поля
Value_T m[N];
// открытые имена, вводимые typedef
typedef Value_T value_type;
typedef Value_T* iterator;
typedef const Value_T* const_iterator;
typedef Value_T& reference;
typedef const Value_T& const_reference;
typedef size_t size_type;
// определение более короткого синонима для kvector
typedef kvector self;
// функции-члены
template<typename Iter_T>
void copy(Iter_T first, Iter_T last) {
copy(first, last, begin());
}
iterator begin() { return m; }
iterator end() { return m + N; }
const_iterator begin() const { return m; }
const_iterator end() const { return m + N; }
reference operator[](size_type n) { return m[n]; }
const_reference operator[](size_type n) const { return m[n]; }
static size_type size() { return N; }
// векторные операции
self& operator+=(const self& x) {
for (int i=0; i<N; ++i) m[i] += x.m[i];
return *this;
}
self& operator-=(const self& x) {
for (int i=0; i<N; ++i) m[i] -= x.m[i];
return *this;
}
// скалярные операции
self& operator=(value_type x) {
std::fill(begin(), end(), x);
return *this;
}
self& operator+=(value_type x) {
for (int i=0; i<N; ++i) m[i] += x;
return *this;
}
self& operator-=(value_type x) {
for (int i=0; i<N; ++i) m[i] -= x;
return *this;
}
self& operator*=(value_type x) {
for (int i=0; i<N; ++i) m[i] *= x;
return *this;
}
self& operator/=(value_type x) {
for (int i=0; i<N; ++i) m[i] /= x;
return *this;
}
self& operator%=(value_type x) {
for (int i=0; i<N; ++i) m[i] %= x;
return *this;
}
self operator-() {
self x;
for (int i=n; i<N; ++i) x.m[i] = -m[i];
return x;
}
// дружественные операторы
friend self operator+(self x, const self& y) { return x += у; }
friend self operator-(self x, const self& y) { return x -= y; }
friend self operator+(self x, value_type y) { return x += y; }
friend self operator-(self x, value_type y) { return x -= y; }
friend self operator*(self x, value_type y) { return x *= y; }
friend self operator/(self x, value_type y) { return x /= y; }
friend self operator%(self x, value type y) { return x %= y; }
};
Пример 11.18 показывает, как можно применять шаблон класса kvector.
Пример 11.18. Применение вектора kvector
#include "kvector.hpp"
#include <algorithm>
#include <numeric>
#include <iostream>
using namespace std;
int main() {
kvector<int, 4> v = { 1, 2, 3, 4 };
cout << "sum = " << accumulate(v.begin(), v.end(), 0) << endl;
v *= 3;
cout << "sum = " << accumulated.begin(), v.end(), 0) << endl;
v += 1;
cout << "sum = " << accumulate(v.begin(), v.end(), 0) << endl;
}
Программа примера 11.18 выдаст следующий результат.
sum = 10
sum = 30
sum = 34
Обсуждение
Представленный в примере 11.17 шаблон kvector является гибридом valarray и шаблона массива, предложенного в TR1. Как и valarray, вектор kvector представляет собой последовательность значений заданного числового типа, однако подобно массиву TR1::array его размер известен на этапе компиляции.
Характерной особенностью шаблона kvector является то, что для его инициализации может использоваться синтаксис, применяемый для массивов, и то, что он имеет функции-члены begin и end. Фактически kvector можно рассматривать как псевдоконтейнер, т.е. он удовлетворяет некоторым, но не всем требованиям концепции стандартного контейнера. Следствие этого — более легкое применение kvector в стандартных алгоритмах по сравнению с valarray.
Другое преимущество шаблонного класса kvector состоит в том, что он поддерживает синтаксис, используемый при инициализации массивов.
int x;
kvector<int, 3> k = { x = 1, x+2, 5}
Этот синтаксис возможен только потому, что kvector является агрегатом. Агрегат (aggregate) — это массив или класс, который не имеет объявленных пользователем конструкторов, закрытых или защищенных данных-членов, базового класса и виртуальных функций. Следует отметить, что все же можно при объявлении kvector его заполнить значениями по умолчанию.
kvector<int, 3> k = {};
В результате этот вектор будет заполнен нулями.
Как вы видите, при его реализации мной был найден компромисс между полным удовлетворением требований, предъявляемых к стандартным контейнерам, и возможностью использования синтаксиса, применяемого при инициализации массивов. Аналогичный компромисс был найден при проектировании шаблона array, удовлетворяющего требованиям TR1.
Возможно, самое большое преимущество kvector над реализациями динамического вектора проявляется в его высокой производительности. По двум причинам шаблон kvector значительно эффективнее, чем большинство реализаций динамических векторов: компиляторы очень хорошо справляются с оптимизацией циклов фиксированною размера, и здесь нет динамического распределения памяти. Различия в производительности особенно проявляются при работе с небольшими матрицами (например, 2?2 или 3?3), которые часто встречаются во многих приложениях.
Что означает имя «self», введенное оператором typedef?
Введенное с помощью typedef имя self я использую в примере 11.17 и в последующих примерах; оно представляет собой удобное краткое имя, которое я использую для ссылки на тип текущего класса. Программу значительно легче писать и воспринимать при использовании self вместо имени класса.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Маленький стек фиксированного размера
Маленький стек фиксированного размера Пользовательские программы могут "отдохнуть" вместе со своими тоннами статически выделяемых переменных в стеке, включая структуры большого размера и многоэлементные массивы. Такое поведение является законным в режиме задачи, так
Текст фиксированного формата
Текст фиксированного формата Аппетит приходит во время еды. Мы еще не успели доделать свою первую Web- страницу, а уже хотим сделать еще одну. Давайте же ее сделаем. Дадим аппетиту разгуляться!Новая Web-страница (листинг 2.10) будет посвящена тегу <TITLE>. Листинг 2.10 <!DOCTYPE
Текст фиксированного формата
Текст фиксированного формата Аппетит приходит во время еды. Мы еще не успели доделать свою первую Web- страницу, а уже хотим сделать еще одну. Давайте же ее сделаем. Дадим аппетиту разгуляться!Новая Web-страница (листинг 2.10) будет посвящена тегу <TITLE>. Листинг 2.10 <!DOCTYPE
8.4.1 Протоколы вектора расстояния
8.4.1 Протоколы вектора расстояния Самый простой протокол для сравнения маршрутизаторов использует счет попаданий между конечными точками пути. Некоторые улучшенные варианты оценивают стоимость или вес каждого из участков по пути следования. Например, участок попадания
3.7. Получение минимального и максимального значений числового типа
3.7. Получение минимального и максимального значений числового типа ПроблемаТребуется узнать наибольшее и наименьшее значения, представляемые на данной платформе числовым типом, таким как int или double.РешениеЧтобы среди прочего получить максимальное и минимальное
6.3. Копирование вектора
6.3. Копирование вектора ПроблемаТребуется скопировать содержимое одного vector в другой.РешениеИмеется пара способов сделать это. Можно при создании vector использовать конструктор копирования, а можно использовать метод assign. Пример 6.3 показывает оба этих способа.Пример 6.3.
11.8. Представление динамического числового вектора
11.8. Представление динамического числового вектора ПроблемаТребуется иметь тип для манипулирования динамическими числовыми векторами.РешениеВы можете использовать шаблон valarray из заголовочного файла <valarray>. Пример 11.15 показывает, как можно использовать шаблон
11.11. Вычисление нормы вектора
11.11. Вычисление нормы вектора ПроблемаТребуется найти норму (т. е. длину) числового вектора.РешениеМожно использовать функцию inner_product из заголовочного файла <numeric> для умножения вектора на самого себя, как показано в примере 11.21.Пример 11.21. Вычисление нормы вектора#include
11.20. Представление больших чисел фиксированного размера
11.20. Представление больших чисел фиксированного размера ПроблемаТребуется выполнить операции с числами, размер которых превышает размер типа long int.РешениеШаблон BigInt в примере 11.38 использует bitset из заголовочного файла <bitset> для того, чтобы можно было представить целые
1.11 Вектора
1.11 Вектора Встроенное в С++ понятие вектора было разработано так, чтобы обеспечить максимальную эффективность выполнения при минимальном расходе памяти. Оно также (особенно когда используется совместно с указателями) является весьма универсальным инструментом для
1.17 Полиморфные Вектора
1.17 Полиморфные Вектора У вас есть другая возможность – определить ваш векторный и другие вмещающие классы через указатели на объекты некоторого класса: class common (* //... *); class vector (* common** v; //... public: cvector(int); common* amp; elem(int); common* amp; operator[](int); //... *);Заметьте, что поскольку в таких
2.3.7 Указатели и Вектора
2.3.7 Указатели и Вектора Указатели и вектора в С++ связаны очень тесно. Имя вектора можно использовать как указатель на его первый элемент, поэтому пример с алфавитом можно было написать так:char alpha[] = «abcdefghijklmnopqrstuvwxyz»; char* p = alpha; char ch;while (ch = *p++) cout «„ chr(ch) „« " = " «« ch «« « = 0“ ««
Создание подстановки из фиксированного набора значений
Создание подстановки из фиксированного набора значений После ввода нескольких записей в таблицу Заказы становится ясно, что в поле СостояниеЗаказаприходится вводить одни и те же значения. Для упрощения ввода данных в это поле можно создать еще одну связанную таблицу,