Страницы

Поиск по вопросам

Показаны сообщения с ярлыком stl. Показать все сообщения
Показаны сообщения с ярлыком stl. Показать все сообщения

воскресенье, 15 марта 2020 г.

Ошибка выполнения при удалении элемента контейнера в цикле

#cpp #алгоритм #stl #vector #вектор


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

#include 
#include 
#include 

using namespace std;

int main(){
    vector v;

    v.push_back("-");
    v.push_back("+");
    v.push_back("-");

    auto it = v.begin();

    for (it; it != v.end(); it++)
        if (*it == "+"){
            v.erase(it); // сдесь ошибка выполнения
        }

    return 0;
}

    


Ответы

Ответ 1



После удаления элемента итераторы становятся не валидными. Правильно будет написать следующим образом (я заменил цикл for на while, так как вы итератор it объявили вне цикла, и цикл while в этом случае смотрится лучше. Хотя лучше использовать цикл for с объявлением итератора внутри цикла) while ( it != v.end() ) if (*it == "+"){ it = v.erase(it); } else { ++it; } Общий подход для такой задачи пишется в одну строчку #include #include #include //... v.erase( std::remove( v.begin(), v.end(), "+" ), v.end() ); Если хотите удалить только один элемент, то можно записать следующим образом: auto it = std::find( v.begin(), v.end(), "+" ); if ( it != v.end() ) v.erase( it );

пятница, 13 марта 2020 г.

Обвертка над итератором для преобразования типа

#cpp #stl #boost #итераторы


Есть два типа A и B, между объектами этих типов установлено соответствие. Есть набор
X значений типа А и итератор по этому набору. Необходимо с минимальными трудозатратами
на основе итератора X получить итератор по значениям типа B.

В общем по-простому говоря, есть контейнер типа A, из итератора А нужно получить
итератор B, потому что клиент принимает итераторы B.

Например:

typedef std::pair A;
typedef float                 B;

B A2B(const A & item) // соответствие между A и B
{
    return item.second;
}

std::vector X;
input_iterator bi = magic_iterator_wrapper(A2B, X.begin());


Чем может быть magic_iterator_wrapper?
    


Ответы

Ответ 1



Можно использовать boost::transform_iterator #include #include #include #include using A = std::pair; using B = float; float A2B(const A & item) { return item.second; } template auto make_transform_iterator(I i, F f) { return boost::transform_iterator{i, f}; } int main() { std::vector
v = {{0, 1}, {0, 20}}; auto first = make_transform_iterator(v.begin(), A2B); auto last = make_transform_iterator(v.end(), A2B); std::cout << std::accumulate(first, last, 0) << '\n'; } >>> То же на реальном компиляторе <<<

пятница, 6 марта 2020 г.

Как правильно записать конструктор класса с полем map?

#cpp #stl #map #шаблоны_с++


//hashtable.h
#pragma once
#include 
#include 
#include 
using namespace std;

template
class hashtable
{
private:
    map *table;
public:
    hashtable(kType k, vType v);
    ~hashtable();
};




//hashtable.cpp
#include "hashtable.h"

template
hashtable::hashtable() 
{
    // ??? Compiler Error C2955 ('hashtable': use of class template requires template
argument list)
}


Could you tell me what is wrong there?

Возможно ли вообще и если да, то как записать map в качестве поля класса?
Так чтобы можно было написать hashtable  и получался ассоциативный массив
с ключем в виде char* и параметром int, но чтобы была возможность задать и другие типы? 

по ТЗ, использовать map надо обязательно
    


Ответы

Ответ 1



Ваша функция должна быть определена как template hashtable::hashtable(kType k, vType v) ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ чтобы соответствовало объявлению в классе

вторник, 18 февраля 2020 г.

Сортировать вектор структур [закрыт]

#cpp #сортировка #cpp11 #stl


        
             
                
                    
                        
                            Закрыт. Этот вопрос необходимо уточнить или дополнить
подробностями. Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Добавьте больше подробностей
и уточните проблему, отредактировав это сообщение.
                        
                        Закрыт 2 года назад.
                                                                                
           
                
        
Отсортировать вектор по одному из полей, используя алгоритмы STL.
По возможности использовать все перегрузки сортировки (фраза, которую я не понял).

struct Dot
{
    double x,y,z;
};

std::vector numbers = {
    {123.0, 48.55, 0},
    {-128.1, 0, 49.55},
    {0,134.0,-59.1}
};


Здесь пример очень похож на правду, но я не могу свести до моей задачи.
    


Ответы

Ответ 1



По возможности использовать все перегрузки сортировки (фраза, которую я не понял). В C++ функции могут быть перегружены - иными словами несколько функций с одним и тем же именем но разной сигнатурой могут одновременно быть определены в коде, и затем вызваны соответствующим образом. В данном случае в роли такой функции должна выступать функция из стандартной библиотеки - std::sort(...) Касательно вашей задачи - чтобы сортировать точки по какому-то полю (или координате), компилятору необходим "объяснить" как сравнивать точки (то есть структуры Dot) - это можно сделать аж тремя способами: Создать кастомный компаратор, перегрузив оператор () вызова функции (устаревший вариант) struct Dot { double x,y,z; }; std::vector numbers = { {123.0, 48.55, 0}, {-128.1, 0, 49.55}, {0,134.0,-59.1} }; struct pred { bool operator()(const Dot& dot1, const Dot& dot2) { return dot1.x < dot2.x; } }; int main() { std::sort(numbers.begin(), numbers.end(), pred()); for (int i = 0; i < numbers.size(); ++i) std::cout << numbers[i].x << " " << numbers[i].y << " " << numbers[i].z << '\n'; return 0; } Перегрузить оператор < //... bool operator < (const Dot& dot1, const Dot& dot2) { return dot1.x < dot2.x; } int main() { std::sort(numbers.begin(), numbers.end()); for (int i = 0; i < numbers.size(); ++i) std::cout << numbers[i].x << " " << numbers[i].y << " " << numbers[i].z << '\n'; return 0; } Передать лямбду (раз уж вы указали 11-ый стандарт) в функцию сортировки из STL //... int main() { std::sort(numbers.begin(), numbers.end(), [](const Dot& dot1, const Dot& dot2) -> bool { return dot1.x < dot2.x; } ); for (int i = 0; i < numbers.size(); ++i) std::cout << numbers[i].x << " " << numbers[i].y << " " << numbers[i].z << '\n'; return 0; } P.S. Инклюды не писал, чтобы сократить код.

воскресенье, 16 февраля 2020 г.

Сортировать вектор структур [закрыт]

#cpp #сортировка #cpp11 #stl


        
             
                
                    
                        
                            Закрыт. Этот вопрос необходимо уточнить или дополнить
подробностями. Ответы на него в данный момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Добавьте больше подробностей
и уточните проблему, отредактировав это сообщение.
                        
                        Закрыт 2 года назад.
                                                                                
           
                
        
Отсортировать вектор по одному из полей, используя алгоритмы STL.
По возможности использовать все перегрузки сортировки (фраза, которую я не понял).

struct Dot
{
    double x,y,z;
};

std::vector numbers = {
    {123.0, 48.55, 0},
    {-128.1, 0, 49.55},
    {0,134.0,-59.1}
};


Здесь пример очень похож на правду, но я не могу свести до моей задачи.
    


Ответы

Ответ 1



По возможности использовать все перегрузки сортировки (фраза, которую я не понял). В C++ функции могут быть перегружены - иными словами несколько функций с одним и тем же именем но разной сигнатурой могут одновременно быть определены в коде, и затем вызваны соответствующим образом. В данном случае в роли такой функции должна выступать функция из стандартной библиотеки - std::sort(...) Касательно вашей задачи - чтобы сортировать точки по какому-то полю (или координате), компилятору необходим "объяснить" как сравнивать точки (то есть структуры Dot) - это можно сделать аж тремя способами: Создать кастомный компаратор, перегрузив оператор () вызова функции (устаревший вариант) struct Dot { double x,y,z; }; std::vector numbers = { {123.0, 48.55, 0}, {-128.1, 0, 49.55}, {0,134.0,-59.1} }; struct pred { bool operator()(const Dot& dot1, const Dot& dot2) { return dot1.x < dot2.x; } }; int main() { std::sort(numbers.begin(), numbers.end(), pred()); for (int i = 0; i < numbers.size(); ++i) std::cout << numbers[i].x << " " << numbers[i].y << " " << numbers[i].z << '\n'; return 0; } Перегрузить оператор < //... bool operator < (const Dot& dot1, const Dot& dot2) { return dot1.x < dot2.x; } int main() { std::sort(numbers.begin(), numbers.end()); for (int i = 0; i < numbers.size(); ++i) std::cout << numbers[i].x << " " << numbers[i].y << " " << numbers[i].z << '\n'; return 0; } Передать лямбду (раз уж вы указали 11-ый стандарт) в функцию сортировки из STL //... int main() { std::sort(numbers.begin(), numbers.end(), [](const Dot& dot1, const Dot& dot2) -> bool { return dot1.x < dot2.x; } ); for (int i = 0; i < numbers.size(); ++i) std::cout << numbers[i].x << " " << numbers[i].y << " " << numbers[i].z << '\n'; return 0; } P.S. Инклюды не писал, чтобы сократить код.

воскресенье, 9 февраля 2020 г.

Для чего нужен reserve() в C++?

#cpp #оптимизация #vector #stl


Не могу понять, в чем смысл функции reserve(). Она выделяет память, но не создает
элементов, увеличивает емкость, но не размер. Для чего она нужна если все можно сделать
с помощью resize()?
    


Ответы

Ответ 1



Для оптимизации. resize требует инициализации всех элементов, и делает размер контейнера строго заказанным. Он становится заполненным чем-то - что вам может быть не нужно в данный момент. При этом вам придется отслеживать отдельно реальную заполненность вашего вектора, т.к. size() будет, по сути, врать - говоря, сколько всего элементов в векторе, а не элементов, нужных вам. Если же вы хотите добавлять с помощью resize() равно столько элементов, сколько вам в данный момент нужно - то это просто бестолку потраченное время на инициализацию и не более того... потому что каждый вызов будет вызывать перераспределение памяти и копирование. reserve подготавливает место для последующего заполнения, так что какой-нибудь push_back будет гарантированно (а не амортизированно) выполняться за O(1) - без каких-либо перераспределений памяти, съедающих массу времени... При этом size() будет давать точное количество элементов, итератор end() показывать куда надо... Примерно так. Если не убедил - могу еще немного поубеждать :)

суббота, 8 февраля 2020 г.

Как устроен и работает предикат?

#cpp #stl


Не могу разобраться как устроен и работает предикат. Вот простенький пример использования
предиката в сортировке:

#include 
#include 
#include 

using namespace std;

bool mySort(int x, int y){ // функция будет использоваться в качестве предиката
    return x < y;
}

int main (){
    vector va;

    va.push_back(10);
    va.push_back(100);
    va.push_back(2);

    // for (vector::iterator it = va.begin(); it != va.end(); it++)
        // cout << *it << endl;

    sort(va.begin(), va.end(), mySort); //третий параметр является предикатом

    // cout << endl;

    // for (vector::iterator it = va.begin(); it != va.end(); it++)
        // cout << *it << endl;

    return 0;
}


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


Ответы

Ответ 1



std::sort третьим параметром принимает что-то, что можно вызвать как функцию имеющую 2 параметра(полный список требований можно посмотреть тут). Не 3, не 1 — именно 2. Почему 2? Потому что для сортировки, за одну операцию, нужно сравнить именно 2 элемента. Теперь касательно типа параметров: чтобы корректно сравнить 2 элемента массива, тип параметров функции должен быть совместим с типом элементов массива. Поэтому у Вас в коде и используется int. Но можете использовать и long и любой другой тип, который даст корректный результат сравнения. Но вот если Вы сделаете параметры функции, к примеру, типа std::string, то получите ошибку компиляции. Для лучшего пониманию, предлагаю рассмотреть следующий пример: #include void myFunc(int x, int y) { std::cout << "x: " << x << ", y:" << y << "\n"; } template void superFunction(Fun fun) { fun(156, 11); } int main(int argc, char* argv[]) { superFunction(myFunc); } Здесь, superFunction чем-то похожа на std::sort, она принимает своим аргументом что-то, что можно вызвать с двумя параметрами, причём каждый параметр является int. Внутри это «что-то» вызывается. Передав в функции main нашу myFunc мы получаем вызов оной, косвенно, через superFunc. Точно так же всё происходит в std::sort, только там переданная функция используется для сравнения двух элементов и в эту функцию передаются два члена массива, вместо констант, как в моём коде.

Ответ 2



Предикат в данном случае - функция сравнения, используемая алгоритмом сортировки. std::sort является шаблоном и компилятор подставит вызов функции-предиката при инстанцировании шаблона. Алгоритм будет менять порядок элементов коллекции основываясь на отношении (полного) порядка, которое и задаётся предикатом. Иными словами, для любых двух элементов коллекции предикат должен "сказать" который из них меньше. В предикате два параметра потому, что алгоритм std::sort при работе сравнивает два элемента. Если использовать какой-либо хитронавороченный алгоритм, то ему может потребоваться и иное число параметров. Тип int у аргументов предиката потому, что контейнер содержит элементы типа int. В общем случае типы не обязаны совпадать, а должны быть приводимыми. например, можно использовать предикат с аргументами типа long или double. Кстати, вместо функций в качестве предикатов можно использовать лямбды (C++11): std::sort(va.begin(), va.end(), [](const int x, const int y) { return x < y });

среда, 5 февраля 2020 г.

Сравнение элементов в контейнерном классе array

#cpp #массивы #stl #сравнение


Проблема следующая, есть класс Person с именем и возрастом. Я создаю 2 контейнера
 array, забивая их какими-то экземплярами, и позже хочу сравнить эти контейнеры, на
что получаю ошибку. Оператор сравнения перегрузил, а на ютубе посмотрел, что мол перегрузки
будет достаточно для сравнения

Серьезность Код Описание    Проект  Файл    Строка  Состояние подавления
Ошибка  C2672   "operator __surrogate_func": не найдена соответствующая перегруженная
функция   prikl_programming   c:\program files (x86)\microsoft visual studio\2017\community\vc\tools\msvc\14.16.27023\include\xutility
   3084    

`#include 
 #include 
 #include 
 using namespace std;

 class Person {
 public:
 Person(int age, string name) {
    this->age = age;
    this->name = name;
 }
 Person() {
    age = 0;
    name = "null";
 }

 bool operator==(const Person& p) {
    if (this->age == p.age && this->name == p.name)
        return true;
    else
        return false;
 }

 private:
 int age;
 string name;

 friend ostream& operator<<(ostream& os, const Person& p);      
 };

 int main() {

 list l = { Person(1, ""), Person(2, ""), Person(3, "") };
 list l2 = { Person(2, ""), Person(2, ""), Person(3, "") };

 bool flag = (l == l2);
введите сюда код
 system("pause");
 return 0;
 }

 ostream & operator<<(ostream & os, const Person & p)
 {
 cout << p.age << " " << p.name;
 return os;
 }`

    


Ответы

Ответ 1



Попробуйте дать компилятору понять, что ваше сравнение не будет ничего менять в сравниваемых объектах - что это константная функция-член: bool operator==(const Person& p) const { if (this->age == p.age && this->name == p.name) return true; else return false; } (обратите внимание на второй const в первой строке).

Ответ 2



А давайте немного обсудим как лучше строить подобный код. Нужно иметь возможность знать имя или возраст личности, а также придать им новые значения поэтому проще эти поля сделать открытыми. И, так как тип не имеет других полей, то он легко сделается обычной структурой, не имеющей закрытых данных. Конструктор по умолчанию будет означать, что нам неизвестно имя и возраст личности. Он может быть определен посредством вызова имеющегося конструктора с конкретными аргументами. Сама личность не должна иметь функциональность сравнения себя с другой личностью. Такие функции лучше вынести за класс. Исходя из этих соображений напишем улучшенный код, где, кстати, не происходят лишные копировния строк и дальнейшее присваивание в конструкторе (как в вашем случаи): struct Person { int age{}; string name; Person(const int age, const string& name) : age(age), name(name) {} Person() : Person(0, "unknown") {} }; inline bool operator ==(const Person& p1, const Person& p2) { return p1.age == p2.age && p1.name == p2.name; } int main() { list l = { Person(1, ""), Person(2, ""), Person(3, "") }; list l2 = { Person(2, ""), Person(2, ""), Person(3, "") }; bool flag = (l == l2); //... } Такой код нагляднее показывает наши намерения и читабельней...

понедельник, 3 февраля 2020 г.

Сколько места занимает std::vector

#cpp #stl #vector


Интересует такой момент сколько места занимает std::vector int на 10x10 элементов в x64?

например просто std::vector на 10 значений занимает 24байта(сам объект) + 10x4байта(10
значений int) = 64байта, 

vector a (10,1);


соответственно 10x10 элементов будет занимать 24 + 24x10 + 10x10x4 = 664байта или
24 + 10x10x4 = 424байта?

vector> и (10,vector  (10,1));


Возник еще один вопрос, если я объявил вектор 

vector> b (10,vector  (10));


но заполнил допустим только один его элемент числом, например 

b[1][1] = 5;


остальные элементы второго вектора будут занимать 4 байта, или пока я не присвоил
им значение, то место занимает только структура вектора? соответственно размер 24 +
24x10 + 4 = 268, или в любом случае будет 664байта?
    


Ответы

Ответ 1



N*N vector> занимает sizeof(vector) + sizeof(vector)*N + sizeof(int)*N*N + α(N) где α(N) - накладные расходы на выделение памяти в хипе (хипу надо хранить сколько там выделено) sizeof(vector) - это обычно 3*sizeof(void*), и он не зависит от типа который хранится в векторе. (Теоретически возможен вектор меньшего размера, но так никто не делает).

Ответ 2



Размер вектора состоит непосредственно из памяти под структуру и выделенной памяти под массив. При том способе, каким ты создаёшь вектора, их вместимость будет фиксированной, т. е. равна тому числу элементов, которое ты запрашиваешь. Выделится сразу вся память, естественно. Исключением является vector, который должен держать значения в битах, а не байтах. В общем случае, насколько я помню, при использовании только операций добавления, можно рассчитывать, что вместимость вектора превосходит количество элементов в нём не более чем в 2 раза. При добавлении и удалении - в 4 раза. http://codepad.org/mTvWj83Z #include #include int main(void) { vector < vector > v(10, vector (10,1)); printf("%d + %d*%d + %d*%d", sizeof v, v.capacity(), sizeof v[0], v[0].capacity(), sizeof v[0][0]); printf(" = %d\n", sizeof v + v.capacity() * sizeof v[0] + v[0].capacity() * sizeof v[0][0]); return 0; } Выводит: 28 + 10*28 + 10*4 = 348

Ответ 3



Размер занимаемой памяти вектором зависит от конкретной реализации класса вектора и размера типа значения, Например размер типа int также может меняться в зависимости от среды, где запускается программа. Если запустить данную тестовую программу #include #include int main() { std::vector> v( 10, std::vector( 10 ) ); size_t size1, size2, size3; std::cout << "sizeof( std::vector> ) = " << ( size1 = sizeof( std::vector> ) ) << std::endl; std::cout << "v.capacity() * sizeof( vector ) = " << ( size2 = v.capacity() * sizeof( std::vector ) ) << std::endl; std::cout << "v[0].capacity() * sizeof( int ) = " << ( size3 = v[0].capacity() * sizeof( int ) ) << std::endl; std::cout << "Total occupied memory size1 + size2 + 10 * size3 = " << size1 + size2 + 10 * size3 << std::endl; } то онлайновый компилятор MS VC++ выдает следующие значения: sizeof( std::vector> ) = 12 v.capacity() * sizeof( vector ) = 120 v[0].capacity() * sizeof( int ) = 40 Total occupied memory size1 + size2 + 10 * size3 = 532 В то время как компилятор gcc 5.2.0 выдает следующий результат: sizeof( std::vector> ) = 24 v.capacity() * sizeof( vector ) = 240 v[0].capacity() * sizeof( int ) = 40 Total occupied memory size1 + size2 + 10 * size3 = 664 Как видите, даже при одинаковом размере типа int размер самого объекта типа std::vector разный для разных компиляторов. В VS VC++ этот размер равен 12 байтам, в то время как в gcc 5.2.0 он равен 24 байтам.

воскресенье, 2 февраля 2020 г.

Как удалять один элемент из multiset, если в контейнере имеются дубликаты?

#cpp #stl #множества #multiset


При использовании метода erase() из мультимножества удаляются все элементы со значением
параметра. Можно, конечно, класть в контейнер пару, где first - номер элемента, second
- нужное значение, тогда дубликатов не будет. 

Но есть ли другое решение проблемы? 

Код:

#include 
#include 

using namespace std;

int main()
{
    multiset a = {1, 1, 3};

    for(int i: a)
        cout << i << " "; // Вывод: 1 1 3

    a.erase(1);

    cout << "\n";
    for(int i: a)
        cout << i << " "; // Вывод: 3

    return 0;
}

    


Ответы

Ответ 1



Работайте с итератором. Находите нужный вам элемент, вернее, ненужный :), вернее, итератор, указывающий на него - и вызывайте erase. С точки зрения multiset все значения с одним и тем же ключом совершенно неотличимы, как какие-нибудь электроны... С вашей - они вполне могут и отличаться, и тогда ваше дело - показать мультимножеству на него итератором и сказать "ату его"...

Ответ 2



Если вам нужно удалить один элемент, делайте erase(find(key)) вместо erase(key). Разумеется, в варианте erase(find(key)) надо добавить проверку на успешность поиска. Однако такой способ удалит "какой-то" из эквивалентных элементов. А уж как вы предлагаете выбирать конкретный элемент для удаления и нужно ли вам его выбирать - об этом вы пока не удосужились сообщить.

пятница, 31 января 2020 г.

Почему пишут “библиотека STL”, но используют пространство имен “std”?

#cpp #stl #терминология


Везде пишут: Использования STL функций..., но в коде мы вызываем std::func(), а не
stl::func(). 
Вопрос: что это такое и как это понять? Возможно кто-то сможет провести аналогию
с пакетами с Java.
Почему пишет STL, а используем std?
    


Ответы

Ответ 1



Название STL было поначалу, означало отдельную, не входящую в тогдашний стандарт библиотеку. Сейчас это просто часть стандартной библиотеки С++. Пространства имен stl никогда не было, есть пространство имен std, в котором находятся функции стандартной библиотеки C++. Примерно так. В общем, название STL не имеет никакой связи с пространством имен, каковым является std.

среда, 29 января 2020 г.

Как правильно переносить(копировать) элемент одного std::vector в другой

#cpp #stl #vector


Интересует как быстрее всего копировать или переносить элементы из одного std::vector
в другой, раньше для возможности быстрого удаления элементов из контейнера использовал
std::list

        for( auto it=l.begin(); it!=l.end();) {
            if ( условие )
                it=l.erase(it);
            else
               ++it;
        }


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

        for( auto it=v.begin(); it!=v.end(); ++it) {
            if ( !условие )
                tmpv.emplace_back(*it);
        }
        v.swap(tmpv);
        tmpv.clear();


сначало элементы которые не попали под условие переношу в другой vector, а потом
меняю контейнеры местами, но мне кажется что

                tmpv.emplace_back(*it);


это не совсем правильно и возможно есть другие функции в std которые позволят переносить(копировать)
элемент быстрее чем реализовано у меня
    


Ответы

Ответ 1



А самый простой вариант (оставляет элементы соответствующие cond()) int n = 0; for (int i = 0; i < v.size(); i++) if (condition(v[i])) v[n++] = v[i]; v.resize(n); не пробовали? Если порядок обработки и относительное расположение элементов в векторе после нее не важны, то процесс можно ускорить раза в полтора (если затраты на вычисление условия, копирование элемента и обработку удаляемого одинаковы). Вот примерчик: #include #include #include #include #include #include #include using namespace std; void act (string s) { cout << s << '\n'; } int main (int ac, char *av[]) { vector v; string s; while (cin >> s) v.push_back(s); cout << "size: " << v.size() << " capacity: " << v.capacity() << '\n'; int n = 0, cntcmp = 0, cntcpy = 0, cntact = 0; if (av[1]) { /* size: 108 capacity: 256 cmp: 120 cpy: 64 act: 140 total: 324 */ n = v.size(); int i = 0, j = v.size() - 1; while (j >= i) { while (i <= j && isalpha(v[i][0])) { cntcmp++; i++; } if (i > j) break; act(v[i]); cntact++; n--; while (j > i && !isalpha(v[j][0])) { cntcmp++; cntact++; act(v[j--]); n--; } if (j == i) break; v[i++] = v[j--]; cntcpy++; } } else { /* size: 108 capacity: 256 cmp: 248 cpy: 108 act: 140 total: 496 */ for (int i = 0; i < v.size(); i++) { cntcmp++; if (isalpha(v[i][0])) { cntcpy++; v[n++] = v[i]; } else { cntact++; act(v[i]); } } } v.resize(n); for (int i = 0; i < n; i++) cout << v[i] << '\n'; cerr << "size: " << v.size() << " capacity: " << v.capacity() << '\n'; cerr << "cmp: " << cntcmp << " cpy: " << cntcpy << " act: " << cntact << " total: " << cntcmp + cntact + cntcpy << '\n'; } Вот результат avp@avp-ubu1:hashcode$ g++ c.cpp avp@avp-ubu1:hashcode$ ./a.out < c.cpp | sort >2.txt size: 108 capacity: 256 cmp: 248 cpy: 108 act: 140 total: 496 avp@avp-ubu1:hashcode$ ./a.out 1 < c.cpp | sort >1.txt size: 108 capacity: 256 cmp: 120 cpy: 64 act: 140 total: 324 avp@avp-ubu1:hashcode$ cmp 1.txt 2.txt avp@avp-ubu1:hashcode$ Ну, все эти sort и cmp для того, чтобы убедиться, что оба варианта дают тот же результат.

Ответ 2



Во-первых, класс std::list имеет специальные функции члены класса remove и remove_if, которые позволяют выполнить данную операцию для списка за один вызов функции: void remove(const T& value); template void remove_if(Predicate pred); Что касается вектора, то я думаю, что вместо того, чтобы удалять каждый элемент, удовлетворяющий заданному условию, по отдельности в цикле, значительно более эффективно использовать стандартный алгоритм std::remove_if в связке с методом вектора erase. Например, v.erase( std::remove_if( v.begin(), v.end(), []( const auto &x ) { return condition( x ); } ), v.end() ); Если же вы с удаляемыми элементами производите какие-то дополнительные операции, то вы просто можете реализацию алгоритма std::remove_if использовать в своем коде в виде цикла и в этот цикл вставить те дополнительные операции, которые вам необходимо проделать над элементами. На мой взгляд это более эффективно, чем копировать элементы вектора в другой вектор, так как это не требует выделение дополнительной памяти и вызова деструкторов для каждого элемента вектора при обмене векторов. И уж по крайней мере если копировать элементы вектора в другой вектор, то лучше использовать move итератор. Проблема может состоять в том, что объекты могут быть не перемещаемы. Что касается этого вызова tmpv.emplace_back(*it); то здесь используется просто конструктор копирования, так как это единственный подходящий конструктор для аргумента *it. Так что никакой разницы между tmpv.emplace_back(*it); и tmpv.push_back(*it); в данном случае нет. Вот демонстрационная программа #include #include struct A { A() { std::cout << "A::A()" << std::endl; } A( const A & ) { std::cout << "A::A( const A &)" << std::endl; } ~A() { std::cout << "A::~A()" << std::endl; } }; int main() { std::vector v1( 1, A() ); std::cout << "-------------------" << std::endl; std::vector v2; v2.emplace_back( *std::begin( v1 ) ); // v2.push_back( *std::begin( v1 ) ); std::cout << "-------------------" << std::endl; } Вывод на консоль: A::A() A::A( const A &) A::~A() ------------------- A::A( const A &) ------------------- A::~A() A::~A()

Ответ 3



Какой-то "размытый" вопрос... Можно например так: std::vector src{ 1, 2, 3, 4, 5 }; std::vector dst; // ... std::copy_if( src.begin(), src.end(), std::back_inserter( dst ), []( const int & i ){ return i > 3; } );

Ответ 4



Насколько я понял, что в вашей задаче происходит копирование по десять элементов. То наверное и нужно копировать по десять элементов сразу. Точно не уверен в скорости моего решения просто проверьте быстрее оно или нет. И сильно не ругайте если я не прав это эксперимент. #include #include #include using namespace std; int main(){ const int size = 50; // размер массива vector a; // из которого копируем vector b; // основной в который пишем srand(time(0)); // рандомизация генератора for (int i = 0; i < size; i++){ // заполняем массив значениями a.push_back(rand() % 100); // из котрого будем копировать } vector::iterator start = a.begin(), end = a.begin(); // переменные для начала и конца копирования for (int i = 0; i < size; i += 10){ // копирование по десять элементов end += 10; // передвигаем счетчик конца на 10 элементов start = end - 10; // находим начало откуда копировать b.resize(i + 10); // увеличиваем вместимость вектора std::copy(start, end, b.begin() + i); // копируем по десять элементов в другой массив // здесь если вы захотите можете удалять каждый десятый элемент как у вас в задании } for (int i = 0; i < size; i++){ // проверка cout << b[i] << endl; } return 0; }

вторник, 28 января 2020 г.

Почему find не находит пробелы в строке?

#cpp #алгоритм #строки #stl


Пытаюсь сосчитать количество пробелов в строке, но через find выводит количество
всех символов, а не пробелов. Почему и какая есть альтернатива?

for(int d=0;d<=s1.length();d++){
   if(s1.find(' ')){
    spaces++;
   }
}

    


Ответы

Ответ 1



Для начала у вас задан неверный диапазон цикла for(int d=0;d<=s1.length();d++){ ^^^ Правильно было бы написать for ( std::string::size_type d = 0; d < s1.length(); d++ ){ ^^^ То, что делает ваш цикл, это подсчитывает количество символов в строке (если только строка не содержит пробел в самом начале), так как в этом случае условие if(s1.find(' ')){ всегда будет истинно. Вы могли бы записать цикл следующим образом for ( std::string::size_type i = 0; i < s1.length(); i++ ) { if ( s1[i] == ' ' /* || s1[i] == '\t' */ ) ++spaces; } Или если вы непременно хотите использовать функцию-член класса find, то for ( std::string::size_type pos = 0; ( pos = s1.find( ' ', pos ) ) != std::string::npos; ++pos ) { spaces++; } Либо вы можете использовать стандартный алгоритм std::count spaces = std::count( s1.begin(), s1.end(), ' ' ); Либо вы можете также подсчитывать и другие пробельные символы, как, например, символ табуляции, используя стандартный алгоритм std::count_if и стандартную C функцию isspace. spaces = std::count_if( s1.begin(), s1.end(), ::isspace ); Для этого вам, естественно, помимо прочих заголовков нужно включить заголовки #include #include Более корректно будет написать spaces = std::count_if( s1.begin(), s1.end(), []( char c ) { return ::isspace( ( unsigned char )c ); } ); так как тип char может вести себя как signed char, и в этом случае если среди символов строки могут встречаться символы с отрицательными значениями кодов, то следует делать приведение к типу unsigned char при вызове isspace.

Ответ 2



Используйте возможности стандартной библиотеки. В данном случае, вам нужен std::count: #include #include #include int main() { std::string str = "a b c"; std::cout<

Ответ 3



for(int d=0;d<=s1.length();d++){ // Число итераций, на один больше числа символов if(s1.find(' ')){ // Вообще-то, проверяется сравнение с string::npos spaces++; // Увеличить значение } } По сути у вас код такой: Увеличить spaces на число символов в строке плюс один, если пробел не на первом месте... То, что вы хотите (поиск в цикле с помощью find), делается так: int main(int argc, const char * argv[]) { string s = "jbcsdbl sdjkbhjskdfb sdfjkbjskdlfb sdfjkh sdjkhjk"; int spaces = 0; for(size_t pos = s.find(' '); pos != string::npos; pos = s.find(' ',pos+1)) spaces++; cout << spaces << endl; }

Не могу понять пример с итератором

#stl #итераторы #cpp


#include 
#include 
#include 
#include 

int main()
{
    std::vector v;
    std::generate_n(std::back_insert_iterator>(v), // can be simplified
                    10, [](){ return std::rand() % 10; });        // with std::back_inserter
    for (int n : v)
        std::cout << n << ' ';
    std::cout << '\n';
}

А точнее эту строчку :
std::back_insert_iterator>(v), // can be simplified
                        10, [](){ return std::rand() % 10; }

и эту  :
    int n : v
Так вот в первом вообще какой-то бред в виде реализации функции в вызове другой функции.
А во втором явно не деление и на тернарный оператор не похоже.
Так что это?
Взял отсюда link    


Ответы

Ответ 1



То, что вам непонятно, появилось отностельно недавно в стандарте C++11 "реализация функции в вызове другой функции" есть не что иное как лямбда-функция - особая форма записи анонимной функции. И нет, это не бред, это действительно удобная штука, без которой ежегодно умирало по сотне маленьких невинных котят. А что касается for (int n : v) то да, это не деление и не тернарный оператор, это так называемый range-based for, аналог foreach из других языков программирования, или более удобоваримая с точки зрения синтаксиса форма работы с итераторами. И то. и другое позволяет писать значительно более краткий и удобочитаемый код - в первом случае программист избавляется от необходимости перегружать код лишними функциями, единственное предназначение которых - быть единожды вызванными в какие-то вспомогательных целях (кстати немаловажный побочный плюс еще и в том, что нет необходимости искать, где эти вспомогательные функции реализованы, так как их реализация находится там, где они используются), во втором - писать громоздкие конструкции с итераторами и begin() end() для элементарной операции итерации по контейнеру. Также с этими двумя близко связано другое нововведение - ключевое слово auto (точнее, его новый смысл) об этих нововведениях можете почитать тут

Ответ 2



Попробуем разобрать частями, написав код в cтаром стиле. std::generate_n(std::back_insert_iterator>(v), // can be simplified 10, [](){ return std::rand() % 10; }); Можно заменить на for(int i = 0; i < 10; ++i){ v.push_back(std::rand() % 10); } Здесь std::generate_n - функция, применяемая для заполнения контейнера. Она принимает указательна начальный итератор, количество элементо и метод реализации. Соответственно std::back_insert_iterator>(v) - итератор, указывающий на место для первого вставляемого элемента, 10 - количество элементов и { return std::rand() % 10; } - метод генерации этих элементов. Перейдем к следующему кусочку. for (int n : v) std::cout << n << ' '; Заменим на: for(vector::iterator it = v.begin(); it != v.end(); ++it){ std::cout << *it << ' '; } Этот цикл служит для обхода контейнера, так называемый foreach. здесь n - конкретный для итерации элемент контейнера v.

Ответ 3



std::generate_n это такая функция из стандартной библиотеки с тремя параметрами. Ничего необычного. Ей, согласно документации, нужно три параметра - итератор для вставки, кол-во этих вставок и функция-генератор значений для вставки. С вторым параметром проблем не должно быть. Первый параметр - итератор для вставки. По простому говоря, это такая обертка для контейнера, которая может добавлять элементы в конец (для вектора она просто дергает push_back). Третий параметр - сложнее. Это обычная лябда-функция. Но их просто нужно изучить. Переводя на привычный язык, этот участок кода запишется так for (int i = 0; i < 10; i++) v.push_back(std::rand() % 10); Что короче, а что красивее - это дело вкуса и требований. for (int n : v) - а это новомодный foreach. Появился в новом стандарте. Он просто последовательно присваивает n значения вектора и обрабатывает в цикле. То есть, просто for (int i = 0; i < v.size(); i++) { int n = v[i]; // }

пятница, 24 января 2020 г.

Map-Reduce на C++ с использованием std::async

#cpp #многопоточность #stl #async #шаблоны_с++


Пытаюсь написать MapReduce.

#include 
#include 
#include 
#include 
#include 

template 
void map(It first, It last, Func f)
{
    while (first != last)
    {
        f(*first++);
    }
}

template 
auto map_reduce(It first, It last, MapFunc f1, FoldFunc f2, size_t num_threads)
    -> decltype(f2(f1(*first), f1(*first)))
{
    size_t block_size = std::distance(first, last) / num_threads;

    std::vector> results(num_threads);
    for (size_t i = 0; i < num_threads; ++i)
    {
        results[i] = std::async(map, first, last, f1);
    }

    return 0;
}


int main()
{
    std::list ls = { 1, 2, 3 };
    auto sum = map_reduce(ls.begin(), ls.end(),
        [](int i) {return i; },
        std::plus(),
        3);

    return 0;
}


Почему компилятор ругается на вызов std::async?

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


Ответы

Ответ 1



Вы передаете шаблонную функцию map в шаблон же, и компилятор не понимает, что туда надо подсунуть. Можно попробовать так: results[i] = std::async(map, first, last, f1); только вот у вас results - это фьючерсы future, а ваша map возвращает void... Если исправить и это, то скомпилироваться она скомпилируется, ну, а что она делать будет - это я не смотрел.

Обобщенные контейнеры без указателя на void

#c #stl #containers


Я тут задумался над следующей проблемой.

Сейчас я работаю над библиотекой обобщенных контейнеров и алгоритмов, делаю это на
C потому, что там, где будет использоваться эта библиотека, плюсы использовать физически
невозможно. Использование GLib так же нежелательно.

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

Я хорошо знаю C++ и STL, поэтому при реализации контейнеров и алгоритмов часто ориентируюсь
на производительность STL как на эталон, чтобы сишный вариант контейнера был не медленнее
и не прожорливее.

Но в некоторых случаях этого добиться непросто.

Например, судя по тестам, std::vector содержит указатель на сплошной блок памяти,
в котором хранятся сами объекты, а не указатели на них. Это без проблем реализуется
и на C. И работает даже немного быстрее, чем плюсовой вариант.

Но можно ли реализовать на C обобщенные связные списки, хэш-таблицы и деревья без
использования лишнего указателя на void?

Например, чтобы узел связного списка содержал не три указателя - prev, next, data,
- а два указателя и блок данных заданного размера и выравнивания?

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

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

Я поковырял GLib, и там практически везде используется простой вариант обобщенного
узла, например, связного списка:

struct s_node
{
    struct s_node *prev, *next;
    void *data;
};


Это же относится к узлам деревьев и хэш-таблиц.

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


Ответы

Ответ 1



Непонятно, в чем загвоздка. Берите и делайте блок с двумя указателями и местом для пользовательских данных. Как вариант, node_header может быть встроенным в пользовательские данные на его стороне, тогда это будет интрузивный список. struct node; typedef struct node node_t; struct node_header; typedef struct node_header node_header_t; struct node_header { node_t * p_prev; node_t * p_next; }; node_t * make_node(size_t const data_size) { char * const p_block = calloc(1, sizeof(node_header_t) + data_size); return (node_t *) p_block; } node_t * get_next(node_t * const p_node) { node_t * p_next = NULL; if(p_node) { p_next = ((node_header_t *) p_node)->p_next; } return p_next; } void * get_data(node_t * const p_node) { void * p_data = NULL; if(p_node) { p_data = (void *)(((char *) p_node) + sizeof(node_header_t)); } return p_data; }

четверг, 9 января 2020 г.

c++ stl: сортировка и копирование

#cpp #сортировка #stl #copy


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

Что-то типа

std::vector data2 = std::sort_copy(data1.begin(), data1.end(), [](int v1, int
v2){...});


Просто не хочется писать следующий код:

std::vector data2 = data1;
std::sort(data1.begin(), data1.end(), [](int v1, int v2){...});


А то начинает страдать чувство прекрасного от лишнего присваивания :)

P.S. исправил ошибку в параметрах sort
    


Ответы

Ответ 1



Вы не только можете копировать в другой контейнер все содержимое первого вектора в отсортированном виде, но и любую отсортированную партию: std::vector data1{84, 44, 12, 23, 34, 8, 7, 4}, data2(data1.size()); auto f1 = data1.begin(), s1 = data1.end(), f2 = data2.begin(), s2 = data2.end(); std::partial_sort_copy(f1, s1, f2, s2); теперь data2 содержит элементы со значением из data1 в отсортированном порядке, а data1 не изменил свое состояние. //data1: 84 44 12 23 34 8 7 4 //data2: 4 7 8 12 23 34 44 84 Например, мы хотим копировать половину отсортированной последовательности, по критерию, отличную от std::less(например в убывающем порядке). Тогда: std::partial_sort_copy(f1, s1, f2, f2 + std::distance(f2,s2)/2, std::greater()); //data2: 84 44 34 23 23 34 44 84

среда, 1 января 2020 г.

Не могу добавить в вектор умный указатель

#cpp #vector #stl #smart_pointer


Имеется класс MyClass, разумеется с конструктором, нужно создать вектор умных указателей
на объекты этого класса. Сам указатель создается, но при попытке добавления в вектор
вылезает ошибка . Что я упустил?

#include 
#include  

using namespace std;
Int main()
{
    vector> vectorPtr;
    unique_ptr p1(new MyClass);
    // до этого момента всё в порядке
    vectorPtr.push_back(p1);
    return 0;
}

    


Ответы

Ответ 1



std::unique_ptr не имеет конструктора копирования, поэтому, чтобы поместить его в вектор, его нужно переместить туда: vectorPtr.push_back(std::move(p1)); Или так: vectorPtr.push_back(std::make_unique()) Либо же создавать прямо в векторе: vectorPtr.emplace_back(new MyClass);

понедельник, 30 декабря 2019 г.

Вывод double с нужной точностью и в нужном формате

#cpp #строки #stl #double #format


Требуется вывести double с максимальной точностью, при этом целую часть, которая
не превышает 999 вывести с пробелами на месте отсутствующих цифр, типа 9,12...

Вывожу следующим образом

std::cout << std::fixed << std::setprecision(std::numeric_limits::digits10
+ 1) << value << std::endl;


Возникло 2 проблемы:

1) везде читал, что точность double определяется как std::numeric_limits::digits10,
однако похоже, что можно вытащить больше значащих цифр, если задать точность в 20 цифр,
то будут видны все 20 отличных от 0 цифр

2) не нашел, как можно задать размер целой части, чтобы заполнить отсутствующие цифры
пробелами

Подскажите, что требуется сделать?
    


Ответы

Ответ 1



Всё, чего вам не хватало, это задать заполнитель setfill и ширину setw выводимого поля: #include #include #include void print(double value) { const auto digits = std::numeric_limits::digits10; std::cout << std::setfill(' ') << std::setw(digits + 4); std::cout << std::fixed << std::setprecision(digits) << value << std::endl; } int main() { double value = 9.12; print(value); } Результат выполнения

Ответ 2



Можно также написать класс, с функциональностью выводить как угодно. Например: #include #include #include using namespace std; class IM { double d; size_t ww; char c; public: IM(double val, size_t whole_size, char imbue = ' ') : d(val), ww(whole_size), c(imbue) {} int get_whole() const { return d; } int get_fraction() const { stringstream s; s << d - get_whole(); int k; s.ignore(2); // пропускаем '0' и '.' s >> k; return k; } friend ostream& operator <<(ostream& os, const IM& m) { os << setw(m.ww) << setiosflags(ios_base::left) <

Ответ 3



Во-первых, смысл величины digits10 для плавающего типа T заключается в том, что если вы возьмете десятичное строковое представление, преобразуете его в значение типа T, а затем преобразуете его из T обратно в десятичное строковое представление, то вы получите digits10 значащих цифр цифр, совпадающих с вашей исходной строкой. Например, если вы используете плавающий тип для хранения целых значений, то целые значения с таким количеством цифр будут представляться без потерь, как и соседние (+-1) целые значения. Родственной величиной является величина max_digits10, которая говорит, что если вы преобразуете плавающее значение типа T в десятичное строковое представление с сохранением max_digits10 старших значащих цифр, а затем преобразуете его обратно в тип T, то вы гарантированно получите исходное значение типа T. Таким образом, величина digits10 описывает сохранение данных в преобразовании туда-обратно вида "строка10 -> плавающее -> строка10". А величина max_digits10 описывает сохранение данных в преобразовании туда-обратно вида "плавающее -> строка10 -> плавающее". Это, однако, совсем не означает, что десятичное строковое плавающее значение будет иметь только столько точных цифр. Огульно обзывать цифры за пределами этого количества "мусором" - очевидная профанация. Какие цифры являются точными, а какие нет - определяется спецификой ваших вычислений и известно только вам. Очевидный пример: "традиционные" двоичные плавающие типы способны точно представлять степени двойки в пределах возможностей экспоненты. То есть std::pow(2, 512) даст вам точное значение типа double 13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096 в котором намного больше значащих цифр, чем digits10 или max_digits10 для double (15 и 17 соответственно). Никакого "мусора" в этом представлении нет (разумеется, если вы хотели вычислить именно 2512) . Вас это не должно удивлять. Умение пользоваться такими возможностями плавающих представлений - это во многом и есть умение пользоваться плавающими типами в общем. Во-вторых, что касается управления шириной поля при выводе - об этом вы уже получили ответы.

Ответ 4



По ходу обсуждения оказалось, что имеет смысл остановиться подробнее на определении точности вещественного типа. Удивительно, но оказывается, что точность double совсем не жалкие 15 цифр, а намного, намного больше! И в подтверждение приводятся числа 2^(-100) и 2^512, которые действительно представляются точно. Так что же, получается что и в самом деле точность double больше, чем 15 цифр? К сожалению нет, такие заявления говорят лишь о непонимании вещественной арифметики. Давайте посмотрим на представление чисел вещественным типом. Точность представления определяется его мантиссой, которая в случае числа двойной точности имеет длину 52 бита и соответственно может представить 2^52 значений. Теперь возьмем некое число, у которого в двоичном представлении биты мантиссы 53, 54, и т.д. являются нулями. Нетрудно видеть, что такое число будет представлено абсолютно точно. С бесконечной точностью! И таких чисел существует ровно 2^52. Попробуем теперь немного уменьшить требования к точности. Возьмем, скажем, тысячу знаков после запятой - есть ли числа, которые представляются с такой точностью? Да, такие числа в самом деле есть, и из свойств множества действительных чисел видно, что их бесконечно много. Какую бы точность мы ни задали - сто, тысячу, миллион знаков после запятой, - оказывается, что существует бесконечно много чисел, представимых именно с такой точностью. Получается, что точность вещественного типа это какая-то странная характеристика, которая произвольно меняется в зависимости от числа вплоть до бесконечности? Нет, именно в этом месте и происходит путаница! Точность представления отдельно взятого числа не имеет никакого отношения к точности вещественного типа. Слово одно, но под ним подразумеваются совершенно разные вещи. А собственно точность представления числа, хотя и кажется очень важной с бытовой точки зрения, в вещественной арифметике никакого значения не имеет. По той простой причине, что она непредсказуема - мы не можем определить точность представления по результату вычислений. Для этого надо этот результат сравнить с эталонным значением, а если мы и так его знаем, то зачем нам что-то вычислять? Так что же такое точность вещественного типа? Представим, что у нас есть два числа, у которых первые 52 бита мантиссы совпадают, а различается бит 53. Эти два числа будут представлены одной и той же мантиссой, и соответственно, тем же самым числом двойной точности. Мы говорим, что точность double составляет 52 бита, и это означает простую вещь: double различает только числа, у которых отличаются первые 52 бита. Числа, у которых отличаются только биты 53 и последующие, представляются одной и той же мантиссой. Вот это и есть точность! Соответственно,в десятичном виде точность double составляет 15.45 десятичных цифр. Это означает, что числа, отличающиеся первыми 15 знаками всегда представляются разными значениями double. Числа с отличиями в 16-ом знаке могут представляться разными значениями (в 45% случаев), а могут и не отличаться (соответственно, в 55% случаев). Числа с отличием в 17-ом и более младших знаках имеют одинаковое представление в числе двойной точности. Разумеется, тема точности вещественной арифметики немного сложнее этого простого определения. Для более серьезного ознакомления можно посоветовать Д. Кнут, том 2 "Получисленные алгоритмы", глава 4.2.2 "Точность выполнения арифметических действий в системе с плавающей точкой".

Освобождение памяти

#cpp #память #stl


Есть вектор указателей на объекты. Как лучше всего освобождать память: free, delete
или erase?
    


Ответы

Ответ 1



Начнем с того, что free категорически противопоказан. Это C-шная функция, работающая только в паре с malloc. erase - это функция, которая удаляет элементы из вектора. Но поскольку у вас вектор указателей, то автоматически память, на которую они указывают, освобождаться не будет (ведь вас это интересует?) Так что надо перед erase пробежаться и освободить объекты самому. Если хотите, чтобы все делалось надежно и просто - используйте unique_ptr (если вектор единолично :) владеет объектами; если нет - shared_ptr<>).

Ответ 2



Есть еще вариант использовать std::vector<Тип>().swap(Ваш_вектор);

Ответ 3



Если ваш объект, содержащий вектор, отвечает за уничтожение хранящихся в нем объектов, то конечно, delete