Добрый день. Подскажите как правильно сделать?
Мне нужно заполнять несколько массивов в одном классе и передать их в другой (там в отдельном потоке их обрабатывать, а потом удалять). Я по старинке делал так (очень примеренный псевдокод):
#include <iostream>
struct Aaa
{
char sz[32];
int a;
};
class cl2
{
public:
void Foo2(uint8_t* ptr, int nCnt)
{
Aaa* pCurAaa = (Aaa*)ptr;
for (int i = 0; i < nCnt; i++)
{
std::cout << pCurAaa->sz << "\t" << pCurAaa->a << std::endl;
pCurAaa++;
}
std::cout << std::endl;
// освобождаем памятьdelete[] ptr;
}
};
cl2 c2;
class cl1
{
public:
uint8_t *m_pAaa;
void Foo1()
{
// несколько раз заполняемfor (int i = 0; i < 5; i++)
{
int nCnt = 10 + i;
int nSize = sizeof(Aaa) * nCnt;
m_pAaa = new uint8_t[nSize];
memset(m_pAaa, 0, nSize);
Aaa* pCurAaa = (Aaa*)m_pAaa;
for (int j = 0; j < nCnt; j++)
{
strcpy_s(pCurAaa->sz, "text");
pCurAaa->a = j;
pCurAaa++; // shift to the next item
}
// передаём в другой класс
c2.Foo2(m_pAaa, nCnt);
m_pAaa = NULL; // всё, тут "забываем" про этот кусок памяти, с ним работает и потом удаляет касс2
}
}
};
void main()
{
cl1 c1;
c1.Foo1();
}
Так вот, вроде как нужно использовать std::vector(), чтобы например не тащить за собой размер передаваемого массива ну и вроде как безопаснее, так?
Если да, то как переписать подобное на использование std::vector()...
PS. И еще один вопрос, а как мне добавить в мою структуру Aaa переменную с типом std::string?
ЗЫЫ. в реальном коде uint8_t *m_pAaa; в классе1 тоже нужна, т.е. с ней работают методы класса1 пока не передали в класс2
Re: Передать массив структур из одного класса в другой
Здравствуйте, .alex, Вы писали:
A>Добрый день. Подскажите как правильно сделать? A>Мне нужно заполнять несколько массивов в одном классе и передать их в другой (там в отдельном потоке их обрабатывать, а потом удалять). Я по старинке делал так (очень примеренный псевдокод):
Нужно почитать что-нибудь по основам языка программирования, на котором вы пишите.
A>Так вот, вроде как нужно использовать std::vector(), чтобы например не тащить за собой размер передаваемого массива ну и вроде как безопаснее, так?
Так удобнее в большинстве случаев.
A>Если да, то как переписать подобное на использование std::vector()...
Что именно у вас не получается?
A>PS. И еще один вопрос, а как мне добавить в мою структуру Aaa переменную с типом std::string?
Точно так же, как и любую другую переменную.
A>ЗЫЫ. в реальном коде uint8_t *m_pAaa; в классе1 тоже нужна, т.е. с ней работают методы класса1 пока не передали в класс2
Не используйте сырые указатели вообще. Они в 99% случаев не нужны. Используйте умные указатели, хотя-бы тот же std::shared_ptr. Примеров в интернете хватает, но стоит разобраться как именно они работают.
Re: Передать массив структур из одного класса в другой
Здравствуйте, .alex, Вы писали:
A>Мне нужно заполнять несколько массивов в одном классе и передать их в другой (там в отдельном потоке их обрабатывать, а потом удалять). Я по старинке делал так (очень примеренный псевдокод): A>Так вот, вроде как нужно использовать std::vector(), чтобы например не тащить за собой размер передаваемого массива ну и вроде как безопаснее, так?
std::vector следует использовать там, где вы заранее не знаете размер массива. Если размер фиксированный и заранее известный, то std::array.
Однако, похоже, что вы передаёте текст, а тогда следует использовать std::string.
A>Если да, то как переписать подобное на использование std::vector()...
Это можно сделать разными способами...
Если переписывать то, что вы делаете (не меняя архитектуры), то, наверно, лучше всего будет использование std::unique_ptr< std::string >
A>PS. И еще один вопрос, а как мне добавить в мою структуру Aaa переменную с типом std::string?
struct Aaa
{
char sz[32];
int a;
std::string value;
};
A>ЗЫЫ. в реальном коде uint8_t *m_pAaa; в классе1 тоже нужна, т.е. с ней работают методы класса1 пока не передали в класс2
Если вам нужно передать массив структур, то можно воспользоваться типом std::unique_ptr< std::vector<Aaa> >
Прежде чем давать более точные советы нужно знать:
— на какой уровень знания C++ следует ориентироваться;
— требования по скорости выполнения;
— какой стандарт C++ доступен для использования;
— природа задачи: потоковая обработка или распараллеливание счётной задачи;
и т.д..
И каждый день — без права на ошибку...
Re: Передать массив структур из одного класса в другой
Здравствуйте, .alex, Вы писали:
A>Добрый день. Подскажите как правильно сделать?
Стандарт какой, C++11 годится?
A>PS. И еще один вопрос, а как мне добавить в мою структуру Aaa переменную с типом std::string?
Вся структура Aaa — это тупо аналог std::string, может от неё просто избавиться?
32-байта как раз и есть Small String Optimization (SSO) в современных С++ на 64-битных платформах. Когда при коротких строках указатель на буфер с текстом и переменная с длинной этого буфера по прямому назначению не используются, а в себе хранят текст длинной до 32-х байт. Это стало частью стандартного С++, после того как с этим давно играли на разные лады https://www.ibm.com/support/pages/small-string-optimized-sso-string-class
Re[2]: Передать массив структур из одного класса в другой
A>Стандарт какой, C++11 годится?
Конечно годится...
A>Вся структура Aaa — это тупо аналог std::string, может от неё просто избавиться? A>32-байта как раз и есть Small String Optimization (SSO) в современных С++ на 64-битных платформах. Когда при коротких строках указатель на буфер с текстом и переменная с длинной этого буфера по прямому назначению не используются, а в себе хранят текст длинной до 32-х байт. Это стало частью стандартного С++, после того как с этим давно играли на разные лады https://www.ibm.com/support/pages/small-string-optimized-sso-string-class
Не, в реальности структура большая, и даже это не структура а класс, там несколько методов и куча других переменных...
Re[2]: Передать массив структур из одного класса в другой
Здравствуйте, B0FEE664, Вы писали: BFE>std::vector следует использовать там, где вы заранее не знаете размер массива. Если размер фиксированный и заранее известный, то std::array. BFE>Однако, похоже, что вы передаёте текст, а тогда следует использовать std::string.
кол-во элементов я заранее не знаю...
A>>Если да, то как переписать подобное на использование std::vector()... BFE>Это можно сделать разными способами... BFE>Если переписывать то, что вы делаете (не меняя архитектуры), то, наверно, лучше всего будет использование std::unique_ptr< std::string >
Почитаю про std::unique_ptr...
BFE> — на какой уровень знания C++ следует ориентироваться;
мой уровень это "С с классами" )), т.е. совсем не высокий...
BFE> — требования по скорости выполнения;
желательно очень быстро, на С и обычными plain массивами все получается очень шустро, но хочется немного компромисса в части скорости написания кода...
BFE> — какой стандарт C++ доступен для использования;
я сварщик не настоящий, для развлечения поставил в настройках студии 17 стандарт, чтобы работали конструкции типа if (int random_num = rand(); random_num % 2 == 0) и еще пользую что-то типа for(v: MyVec), но больше особо стандарты не различаю...
BFE> — природа задачи: потоковая обработка или распараллеливание счётной задачи;
вкратце, получение одним потоком рекордсетов из БД, вторым — обработка этих рекордсетов и файловый ввод-вывод...
Re[2]: Передать массив структур из одного класса в другой
Здравствуйте, SaZ, Вы писали:
SaZ>Нужно почитать что-нибудь по основам языка программирования, на котором вы пишите.
я как понимаю надо про std::unique_ptr и std::shared_ptr почитать, верно?
A>>Если да, то как переписать подобное на использование std::vector()...
SaZ>Что именно у вас не получается?
Нет понимания как сделать указатель на вектор, чтобы его потом передать в другой класс без копирования данных и потом обнулить указатель в первом классе и опять выделать место для нового вектора...
Что я пытался в своем псевдокоде изобразить... std::vector<Acc> *pVec = new... вот что дальше, чтобы конструкторы все вызвались ну пр...
Да и объясняю я наверно не шибко понятно... Наверно у меня представления несколько старые... из С...
Re[3]: Передать массив структур из одного класса в другой
Здравствуйте, .alex, Вы писали:
A>Не, в реальности структура большая, и даже это не структура а класс, там несколько методов и куча других переменных...
Заполнять вектор можно через https://en.cppreference.com/w/cpp/container/vector/emplace
Если у этой структуры-класса есть конструктор с аргументами, то они передаются в std::vector::emplace, внутри которого и вызовется уже создание класса (внутри heap, т.е. через new).
Соответственно, деструктор std::vector очистит за собой всю память (в которой создавал экземпляры Aaa). Это же самое сделает и в случае вызова std::vector::clear();
struct Aaa
{
char sz[32];
int a;
Aaa(const char* sz_, int a ) : sz(sz_), a(a_)
{}
};
class cl2
{
public:
void Foo2(const std::vector<Aaa>& arr)
{
for (const auto& curAaa : arr)
{
std::cout << curAaa.sz << "\t"
<< curAaa.a << std::endl;
}
std::cout << std::endl;
}
};
class cl1
{
public:
void Foo1()
{
std::vector<Aaa> arr;
arr.reserve(5); // не обязательно, но полезноfor (int i = 0; i < 5; i++)
{
for (int j = 0; j < nCnt; j++)
{
arr.emplace( arr.end(), "text", j );
}
c2.Foo2( arr );
arr.clear();
}
}
};
Условно, вызов std::vector::reserve() выделит память в heap заранее без инициализации, чтобы не дёргать new во время каждого emplace. На пяти элементах это фигня, а вот на десятках элементов оно полезно.
Re[4]: Передать массив структур из одного класса в другой
Здравствуйте, a7d3, Вы писали:
A>Заполнять вектор можно через https://en.cppreference.com/w/cpp/container/vector/emplace A>Если у этой структуры-класса есть конструктор с аргументами, то они передаются в std::vector::emplace, внутри которого и вызовется уже создание класса (внутри heap, т.е. через new). A>Соответственно, деструктор std::vector очистит за собой всю память (в которой создавал экземпляры Aaa). Это же самое сделает и в случае вызова std::vector::clear();
A>
A>struct Aaa
A>{
A> char sz[32];
A> int a;
A> Aaa(const char* sz_, int a ) : sz(sz_), a(a_)
A> {}
A>};
A>class cl2
A>{
A>public:
A> void Foo2(const std::vector<Aaa>& arr)
A> {
A> for (const auto& curAaa : arr)
A> {
A> std::cout << curAaa.sz << "\t"
A> << curAaa.a << std::endl;
A> }
A> std::cout << std::endl;
A> }
A>};
A>class cl1
A>{
A>public:
A> void Foo1()
A> {
A> std::vector<Aaa> arr;
A> arr.reserve(5); // не обязательно, но полезно
A> for (int i = 0; i < 5; i++)
A> {
A> for (int j = 0; j < nCnt; j++)
A> {
A> arr.emplace( arr.end(), "text", j );
A> }
A> c2.Foo2( arr );
A> arr.clear();
A> }
A> }
A>};
A>
A>Условно, вызов std::vector::reserve() выделит память в heap заранее без инициализации, чтобы не дёргать new во время каждого emplace. На пяти элементах это фигня, а вот на десятках элементов оно полезно.
Спасибо!
Да, про reserve() в курсе конечно, чтобы переаллокаций не было...
Но я хотел немного не то...
Идея была в том что в Классе1 хранится только указатель на кусок памяти/вектор...
Т.е. я в Класс1::Foo() выделил память, и этот указатель на неё теперь указывает, потом в классе1 я заполнил этот кусок памяти/вектор, еще какие-то методы Класса1 с ним поработали (в реальности его отображает виртуальный CListCtrl через GetDispInfo()), потом нажали кнопку и я просто передаю этот указатель в другой класс2 и уже он начинает работать с этим куском памяти/вектором. А в классе1 я опять выделяю новую память и указатель Класса1 уже указывает на него(новый кусок), заполняю/работаю/передаю и так несколько раз...
Re[5]: Передать массив структур из одного класса в другой
Здравствуйте, .alex, Вы писали: A>Идея была в том что в Классе1 хранится только указатель на кусок памяти/вектор... A>Т.е. я в Класс1::Foo() выделил память, и этот указатель на неё теперь указывает, потом в классе1 я заполнил этот кусок памяти/вектор, еще какие-то методы Класса1 с ним поработали (в реальности его отображает виртуальный CListCtrl через GetDispInfo()), потом нажали кнопку и я просто передаю этот указатель в другой класс2 и уже он начинает работать с этим куском памяти/вектором. А в классе1 я опять выделяю новую память и указатель Класса1 уже указывает на него(новый кусок), заполняю/работаю/передаю и так несколько раз...
Я видимо очень сумбурно объяснил? Или просто так не делают?
Re[3]: Передать массив структур из одного класса в другой
Здравствуйте, .alex, Вы писали:
A>Нет понимания как сделать указатель на вектор, чтобы его потом передать в другой класс без копирования данных и потом обнулить указатель в первом классе и опять выделать место для нового вектора... A>Что я пытался в своем псевдокоде изобразить... std::vector<Acc> *pVec = new... вот что дальше, чтобы конструкторы все вызвались ну пр... A>Да и объясняю я наверно не шибко понятно... Наверно у меня представления несколько старые... из С...
Не надо вектор выделять динамически.
Чтобы дешево перекинуть данные из одного вектора в другой надо использовать move-семантику (С++ 11 и выше) или swap (до C++ 11).
Вариант с move:
void Foo2(std::vector<Aaa>&& arg)
{
std::vector<Aaa> local = std::move(arg);
...
}
Здравствуйте, .alex, Вы писали:
A>Т.е. я в Класс1::Foo() выделил память, и этот указатель на неё теперь указывает, потом в классе1 я заполнил этот кусок памяти/вектор, еще какие-то методы Класса1 с ним поработали (в реальности его отображает виртуальный CListCtrl через GetDispInfo()), потом нажали кнопку и я просто передаю этот указатель в другой класс2 и уже он начинает работать с этим куском памяти/вектором. А в классе1 я опять выделяю новую память и указатель Класса1 уже указывает на него(новый кусок), заполняю/работаю/передаю и так несколько раз...
struct Aaa
{
char sz[32];
int a;
Aaa(const char* sz_, int a ) : sz(sz_), a(a_)
{}
};
using vector_aaa_t = std::vector<Aaa>;
using ptr_set_t = std::shared_ptr< vector_aaa_t >;
class cl2
{
public:
void Foo2(ptr_set_t arr)
{
for (const auto& curAaa : *arr)
{
std::cout << curAaa.sz << "\t"
<< curAaa.a << std::endl;
}
std::cout << std::endl;
}
};
class cl1
{
public:
void Foo1()
{
auto spArr = std::make_shared<vector_aaa_t>();
spArr->reserve(5);
for (int i = 0; i < 5; i++)
{
for (int j = 0; j < nCnt; j++)
{
spArr->emplace( arr.end(), "text", j );
}
c2.Foo2( spArr );
spArr->clear();
}
}
};
Re: Передать массив структур из одного класса в другой
Все спасибо, разобрался. Сделал через emplace и std::move()...
Кстати сравнил с вариантом через new... new, быстрее, но незначительно, а код конечно с векторами более читабельный и лаконичный...
Re[2]: Передать массив структур из одного класса в другой
Здравствуйте, .alex, Вы писали:
A>Кстати подумал, что все-таки нужно себя проверить... Посмотрите пожалуйста код, так нужно делать, в смысле все ли корректно?
struct Aaa
{
char sz[32];
int a;
std::string s;
Aaa(const char* sz_, int a_, std::string str) : a(a_), s(str)
{
strcpy_s(sz, sz_); // <<< почему не strncpy c sizeof(sz) или вообще std::string использовать?
}
};
class cl2
{
public:
void Foo2(std::vector<Aaa>& arg) // <<< если уж внутри std::move, то и тут лучше &&
{
std::vector<Aaa> local = std::move(arg);
for (const auto& a : local)
{
std::cout << a.sz << "\t" << a.a << "\t" << a.s << std::endl;
}
std::cout << std::endl;
}
};
cl2 c2;
class cl1
{
public:
std::vector<Aaa> vAaa;
void Foo1()
{
vAaa.reserve(50); // <<< вот тут уже баг, резерв только для первого вектора (память уедет при первом вызове Foo2)for (int i = 0; i < 50; i++)
{
int nCnt = 10 + i;
for (int j = 0; j < nCnt; j++)
{
vAaa.emplace(vAaa.end(), "sz", j, "string"); // <<< почему не emplace_back?
}
c2.Foo2(vAaa); // <<< тут использовать std::move(vAaa)
}
}
};
Re[4]: Передать массив структур из одного класса в другой
Q>strcpy_s(sz, sz_); // <<< почему не strncpy c sizeof(sz) или вообще std::string использовать?
тут просто что-то написал, это не реальный код...
Q>void Foo2(std::vector<Aaa>& arg) // <<< если уж внутри std::move, то и тут лучше &&
... Q>c2.Foo2(vAaa); // <<< тут использовать std::move(vAaa)
А вот про && не знал... Что-то непонятно про эти l/r-value ссылки... Правильно я понял что && это типа const std::vector<Aaa>& arg?
И в чем отличие если писать с2.Foo2(std::move(vAaa))?
Q>vAaa.reserve(50); // <<< вот тут уже баг, резерв только для первого вектора (память уедет при первом вызове Foo2)
Да, ошибся... Q>vAaa.emplace(vAaa.end(), "sz", j, "string"); // <<< почему не emplace_back?
тоже ошибся
Да, с Си'шными указателями как-то все понятнее — выделил кусок памяти и гоняй его куда хочешь, главное не забыть удалить)
Re[5]: Передать массив структур из одного класса в другой
Здравствуйте, .alex, Вы писали: A>А вот про && не знал... Что-то непонятно про эти l/r-value ссылки... Правильно я понял что && это типа const std::vector<Aaa>& arg? A>И в чем отличие если писать с2.Foo2(std::move(vAaa))?
Нет, это не const X&.
&& помечает, что объект или временный, или больше не нужен.
std::move помечает, что эта именованная переменная переменная больше не нужна и можно ее кишки переместить в другое место без потери смысла.
Программы пишутся для людей, а не для компилятора.
Когда программист видит в сигнатуре функции &&, то ему становится все понятно, что переданный объект уезжает с концами.
Плохо только в С++ имхо, что в шаблонах && другой смысл имеет...
Re[3]: Передать массив структур из одного класса в другой
А еще один вопрос вдогонку) Что-то начитался про конструкторы копирования и перемещения и еще больше запутался... теперь все думаю не будет ли лишнего new вызвано)
Может стоит в конструкторе моего класса написать
... A> Aaa(const char* sz_, int a_, const std::string & str) : a(a_), s(str)
А?
Строка большая и все это вариант без small string optimization...
Re[4]: Передать массив структур из одного класса в другой
Здравствуйте, .alex, Вы писали:
A>А еще один вопрос вдогонку) Что-то начитался про конструкторы копирования и перемещения и еще больше запутался... теперь все думаю не будет ли лишнего new вызвано) A>Может стоит в конструкторе моего класса написать A>... A>> Aaa(const char* sz_, int a_, const std::string & str) : a(a_), s(str) A>А? A>Строка большая и все это вариант без small string optimization...
Хотя интересен ответ и с маленько строкой (sso)
Re[5]: Передать массив структур из одного класса в другой
Здравствуйте, .alex, Вы писали:
A>Здравствуйте, .alex, Вы писали:
A>>А еще один вопрос вдогонку) Что-то начитался про конструкторы копирования и перемещения и еще больше запутался... теперь все думаю не будет ли лишнего new вызвано)
Сейчас у тебя написано так:
std::string s;
Aaa(std::string str) : s(str) ...
В таком коде всегда будет копирование строки в конструкции s(str): будет вызван copy constructor, который сконструирует новую строку и скопирует туда содержимое старой, а потом старая разрушится.
Плюс, есть ещё такой недостаток, что например при вызове Aaa("foobar") сначала сконструируется временная строка в куче, а потом с неё будет сделана копия (с ещё одним выделением памяти).
Чтобы не было одного копирования нужно было написать конструктор так:
Aaa(std::string str) : s(std::move(str)) ...
Тогда, если теперь вызвать Aaa("foobar"), то содержимое временной строки переместится, то есть одно выделение памяти пропадёт.
Соответственно, если у тебя есть уже готовый объект std::string a, то вызов Aaa(a) сначала позовёт copy-constructor, а затем move-constructor. А вызов Aaa(std::move(a)) позовёт два раза move-constructor (один раз чтобы переместить строку из a в параметр конструктора, а второй раз чтобы переместить её оттуда в объект).
Если move-constructor дорогой (а не у всех классов он дёшев), то от него часто бывает полезно избавится, написав два конструктора отдельно для обоих случаев:
Но тут, конечно, видна проблема, что если параметров много, то нужно либо выписывать много конструкторов, либо делать его шаблонным.
A>>Может стоит в конструкторе моего класса написать A>>... A>>> Aaa(const char* sz_, int a_, const std::string & str) : a(a_), s(str) A>>А?
В целом до С++11 так и писали. Но тут всегда будет копирование. Поэтому почти все и использовали copy-on-write реализации строк, в которых копирование в таких ситуациях не приводило к перевыделению памяти, а лишь меняло счётчик ссылок. Если же строка не copy-on-write (а теперь они все такие), то такой код будет не очень хорошим.
Но он станет хорошим, если к нему добавить парный конструктор, принимающий std::string&&, как показано выше.
A>>Строка большая и все это вариант без small string optimization... A>Хотя интересен ответ и с маленько строкой (sso)
Тебе определённо стоит посмотреть рекомендации по передачи параметров в С++ core guidelines.
Но вывод такой:
Если класс имеет дешёвый move-конструктор, то можно писать так:
Aaa(std::string str) : s(std::move(str))
Если класс имеет дорогой move-конструктор, то лучше писать так:
Aaa(const std::string& str) : s(str)
Aaa(std::string&& str) : s(std::move(str))
// или шаблонный аналог вместо двух вариантов
У std::string move-конструктор не дорогой, но и не совсем тривиальный (как раз из-за необходимости ветвления из-за SSO).
И пока не у всех современных компиляторов ещё хватает ума опитимизировать серии таких перемещений.
Поэтому, из последних двух вариантов оба можно использовать ("лишних" выделений памяти не будет ни в одном, ни в другом), но вариант с двумя конструкторами иногда оказывается на пару наносекунд быстрее, так как в нём компилятору проще понять логику происходящего и не надо генерировать код для промежуточного move-конструктора.