Re[7]: Передать массив структур из одного класса в другой
От: qaz77  
Дата: 09.12.20 09:23
Оценка: 4 (2) +1
Здравствуйте, .alex, Вы писали:
A>А как определить что такое дорогой move конструктор?

Если в классе много данных непосредственно, а не по указателям, то move-конструктор вынужден их просто скопировать.

Например, такой класс:
class Bar
{
  int memb0_;
  //...
  int memb99_;

  char text_[200];
  double coef_[30];

  std::vector<std::string> names_;

  // ...
};

Тут только вектор дешево переместится, а инты и массивы будут скопированы — вариантов нет.

Для всех стандартных контейнеров перемещение относительно дешевое.
У вектора — пара указателей и capacity. Примерно можно оценить по sizeof класса.
Re[5]: Передать массив структур из одного класса в другой
От: a7d3  
Дата: 02.12.20 09:28
Оценка: 3 (1)
Здравствуйте, .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[7]: Передать массив структур из одного класса в другой
От: watchmaker  
Дата: 08.12.20 18:06
Оценка: 3 (1)
Здравствуйте, .alex, Вы писали:

A>Здравствуйте, watchmaker, Вы писали:



A> вопрос, получается std::move() правильно писать и при вызове метода и внутри этого метода, так?

Ну в целом да. Если хочешь передать владение, то нужно писать move. Если хочешь это сделать по цепочке, то аннотировать так придётся каждый шаг.
(разумеется, тут есть варианты, когда цепочка функций принимает lvalue-ссылки, но по их названию понятно, что они будут делать в конце похищение ресурсов, тогда можно как бы и без постоянного повторения move код написать, но это всё не про подход, который нужно использовать по умолчанию).


---------------------------------------
Ну и заодно ещё скажу, что в этом фрагменте кода есть проблема:
A>        std::vector<Aaa> vAaa;
A>        for (int i = 0; i < 500; i++)
A>        {
A>            int nCnt = 10 + i;
A>            vAaa.reserve(nCnt);

A>            for (int j = 0; j < nCnt; j++)
A>            {
A>                vAaa.emplace_back("sz", j, "string");
A>            }
A>            c2.Foo2(std::move(vAaa));
A>        }


Тут подразумевается, что в начале каждой итерации внешнего цикла массив vAaa пуст.
Но мне кажется, что тут скорее случайно у тебя так получилось
И этот код развалится, если чуть-чуть поменять тело Foo2, например.

Дело в том, что в С++ объекты, из которых сделали перемещение, обычно остаются в "valid but unspecified state": https://en.cppreference.com/w/cpp/utility/move
И к стандартным контейнерам это тоже относится.

То есть очень неправильно полагаться на то, что после вызова c2.Foo2(std::move(vAaa)); вектор vAaa опустеет. Это как раз может быть не так. И есть много сценариев, когда он после этого может оказаться заполненным какими-то объектами из предыдущих итераций или типа того. Что конкретно произойдёт зависит от внутреннего устройства функции Foo2. И это как раз проблема, так как в этой точке его не видно, и оно может меняться без изменения сигнатуры. Короче говоря, в будущем тут легко может реализоваться баг при редактировании каких-то совсем других функций, который вызываются из Foo2.


Поэтому, если тебе хочется использовать контейнер, из которого сделали move, то нужно его перевести из "unspecified state" в собственно "specified state". Для вектора это означает, что нужно вызвать в начале каждой итерации vAaa.clear()(и потом уже vAaa.reserve(nCnt) и прочее).

Хотя, конечно, ещё и непонятно зачем ты тут используешь vAaa общий для класса, а не создаёшь локальный в функции. Но, возможно, это издержки примера и в реальном коде есть какая-то веская причина (потому что в текущем примере её нет ).
Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 30.11.20 08:41
Оценка: :)
Добрый день. Подскажите как правильно сделать?
Мне нужно заполнять несколько массивов в одном классе и передать их в другой (там в отдельном потоке их обрабатывать, а потом удалять). Я по старинке делал так (очень примеренный псевдокод):
#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: Передать массив структур из одного класса в другой
От: SaZ  
Дата: 30.11.20 12:32
Оценка:
Здравствуйте, .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: Передать массив структур из одного класса в другой
От: B0FEE664  
Дата: 30.11.20 12:58
Оценка:
Здравствуйте, .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: Передать массив структур из одного класса в другой
От: a7d3  
Дата: 30.11.20 14:11
Оценка:
Здравствуйте, .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]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 30.11.20 17:09
Оценка:
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]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 30.11.20 17:19
Оценка:
Здравствуйте, 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]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 30.11.20 17:23
Оценка:
Здравствуйте, SaZ, Вы писали:

SaZ>Нужно почитать что-нибудь по основам языка программирования, на котором вы пишите.

я как понимаю надо про std::unique_ptr и std::shared_ptr почитать, верно?

A>>Если да, то как переписать подобное на использование std::vector()...


SaZ>Что именно у вас не получается?

Нет понимания как сделать указатель на вектор, чтобы его потом передать в другой класс без копирования данных и потом обнулить указатель в первом классе и опять выделать место для нового вектора...
Что я пытался в своем псевдокоде изобразить... std::vector<Acc> *pVec = new... вот что дальше, чтобы конструкторы все вызвались ну пр...
Да и объясняю я наверно не шибко понятно... Наверно у меня представления несколько старые... из С...
Re[3]: Передать массив структур из одного класса в другой
От: a7d3  
Дата: 01.12.20 01:50
Оценка:
Здравствуйте, .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]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 01.12.20 09:39
Оценка:
Здравствуйте, 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 Ниоткуда  
Дата: 02.12.20 07:17
Оценка:
Здравствуйте, .alex, Вы писали:
A>Идея была в том что в Классе1 хранится только указатель на кусок памяти/вектор...
A>Т.е. я в Класс1::Foo() выделил память, и этот указатель на неё теперь указывает, потом в классе1 я заполнил этот кусок памяти/вектор, еще какие-то методы Класса1 с ним поработали (в реальности его отображает виртуальный CListCtrl через GetDispInfo()), потом нажали кнопку и я просто передаю этот указатель в другой класс2 и уже он начинает работать с этим куском памяти/вектором. А в классе1 я опять выделяю новую память и указатель Класса1 уже указывает на него(новый кусок), заполняю/работаю/передаю и так несколько раз...
Я видимо очень сумбурно объяснил? Или просто так не делают?
Re[3]: Передать массив структур из одного класса в другой
От: qaz77  
Дата: 02.12.20 07:34
Оценка:
Здравствуйте, .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);
   ...
}


Вариант со swap:
void Foo2(std::vector<Aaa>& arg)
{
   std::vector<Aaa> local;
   local.swap(arg);
   ...
}
Re: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 03.12.20 07:20
Оценка:
Все спасибо, разобрался. Сделал через emplace и std::move()...
Кстати сравнил с вариантом через new... new, быстрее, но незначительно, а код конечно с векторами более читабельный и лаконичный...
Re[2]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 04.12.20 09:11
Оценка:
Кстати подумал, что все-таки нужно себя проверить... Посмотрите пожалуйста код, так нужно делать, в смысле все ли корректно?
#include <iostream>
#include <vector>

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_);
    }
};

class cl2
{
public:
    void Foo2(std::vector<Aaa>& arg)
    {
        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);
        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");
            }
            c2.Foo2(vAaa);
        }
    }
};

void main()
{
    cl1 c1;
    c1.Foo1();
}
Re[3]: Передать массив структур из одного класса в другой
От: qaz77  
Дата: 04.12.20 10:03
Оценка:
Здравствуйте, .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]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 04.12.20 14:59
Оценка:
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]: Передать массив структур из одного класса в другой
От: qaz77  
Дата: 04.12.20 15:33
Оценка:
Здравствуйте, .alex, Вы писали:
A>А вот про && не знал... Что-то непонятно про эти l/r-value ссылки... Правильно я понял что && это типа const std::vector<Aaa>& arg?
A>И в чем отличие если писать с2.Foo2(std::move(vAaa))?

Нет, это не const X&.
&& помечает, что объект или временный, или больше не нужен.
std::move помечает, что эта именованная переменная переменная больше не нужна и можно ее кишки переместить в другое место без потери смысла.

Программы пишутся для людей, а не для компилятора.
Когда программист видит в сигнатуре функции &&, то ему становится все понятно, что переданный объект уезжает с концами.

Плохо только в С++ имхо, что в шаблонах && другой смысл имеет...
Re[3]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 07.12.20 15:45
Оценка:
А еще один вопрос вдогонку) Что-то начитался про конструкторы копирования и перемещения и еще больше запутался... теперь все думаю не будет ли лишнего new вызвано)
Может стоит в конструкторе моего класса написать
...
A> Aaa(const char* sz_, int a_, const std::string & str) : a(a_), s(str)
А?
Строка большая и все это вариант без small string optimization...
Re[4]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 07.12.20 15:47
Оценка:
Здравствуйте, .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]: Передать массив структур из одного класса в другой
От: watchmaker  
Дата: 07.12.20 17:55
Оценка:
Здравствуйте, .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 дорогой (а не у всех классов он дёшев), то от него часто бывает полезно избавится, написав два конструктора отдельно для обоих случаев:
Aaa(const std::string& str) : s(str) ...
Aaa(std::string&& str) : s(std::move(str)) ...


Но тут, конечно, видна проблема, что если параметров много, то нужно либо выписывать много конструкторов, либо делать его шаблонным.


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-конструктора.
Re[6]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 08.12.20 15:52
Оценка:
Здравствуйте, watchmaker, Вы писали:

Спасибо! Есть что поизучать...
Еще один глупый вопрос, получается std::move() правильно писать и при вызове метода и внутри этого метода, так?
#include <iostream>
#include <vector>
#include <Windows.h>

struct Aaa
{
    char sz[32];
    int a;
    std::string s;

    Aaa(const char* sz_, int a_, std::string str) : a(a_), s(std::move(str))
    {
        strcpy_s(sz, sz_);
    }
};

class cl2
{
public:
    void Foo2(std::vector<Aaa>&& arg)
    {
        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()
    {
        
        for (int i = 0; i < 500; i++)
        {
            int nCnt = 10 + i;
            vAaa.reserve(nCnt);

            for (int j = 0; j < nCnt; j++)
            {
                vAaa.emplace_back("sz", j, "string");
            }
            c2.Foo2(std::move(vAaa));
        }
    }
};

void main()
{
    DWORD dw = GetTickCount();
    cl1 c1;
    c1.Foo1();
    std::cout << GetTickCount() - dw << std::endl;
}
Re[8]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 09.12.20 07:20
Оценка:
W>Тут подразумевается, что в начале каждой итерации внешнего цикла массив vAaa пуст.
W>Но мне кажется, что тут скорее случайно у тебя так получилось
W>И этот код развалится, если чуть-чуть поменять тело Foo2, например.
W>Дело в том, что в С++ объекты, из которых сделали перемещение, обычно остаются в "valid but unspecified state": https://en.cppreference.com/w/cpp/utility/move
W>И к стандартным контейнерам это тоже относится.
W>То есть очень неправильно полагаться на то, что после вызова c2.Foo2(std::move(vAaa)); вектор vAaa опустеет. Это как раз может быть не так. И есть много сценариев, когда он после этого может оказаться заполненным какими-то объектами из предыдущих итераций или типа того. Что конкретно произойдёт зависит от внутреннего устройства функции Foo2. И это как раз проблема, так как в этой точке его не видно, и оно может меняться без изменения сигнатуры. Короче говоря, в будущем тут легко может реализоваться баг при редактировании каких-то совсем других функций, который вызываются из Foo2.
W>Поэтому, если тебе хочется использовать контейнер, из которого сделали move, то нужно его перевести из "unspecified state" в собственно "specified state". Для вектора это означает, что нужно вызвать в начале каждой итерации vAaa.clear()(и потом уже vAaa.reserve(nCnt) и прочее).
Да, я обратил на это внимание и как раз поэтому спрашивал где нужно писать std::move(), т.к. если я писал std::move() только в c2.Foo2(std::move(vAaa)); и не писал его самом методе при std::vector<Aaa> local = arg;, то в вектор у меня не очищался... Ну а если пишу два раза и при вызове и при присваивании или просто при присваивании, то все было ок — очищался... Ладно буду писать std::move() везде и делать clear перед reserve на каждой итерации... (однако, опять хочется сказать, что в Си попроще с указателями))

W>Хотя, конечно, ещё и непонятно зачем ты тут используешь vAaa общий для класса, а не создаёшь локальный в функции. Но, возможно, это издержки примера и в реальном коде есть какая-то веская причина (потому что в текущем примере её нет ).

В реальном коде этот вектор содержит результат рекордсета из БД и отображается в виртуальном лист контроле (поэтому и общий для класса), а как нажали кнопку — уезжает в другой класс для обработки, а на его место помещается новый результат запроса из БД и также отображается и потом уезжает...
Re[6]: Передать массив структур из одного класса в другой
От: .alex Ниоткуда  
Дата: 09.12.20 07:32
Оценка:
W>Но вывод такой:
W>Если класс имеет дешёвый move-конструктор, то можно писать так:
W>
W>Aaa(std::string str) : s(std::move(str)) 
W>

W>Если класс имеет дорогой move-конструктор, то лучше писать так:
W>
W>Aaa(const std::string& str) : s(str) 
W>Aaa(std::string&& str) : s(std::move(str)) 
W>// или шаблонный аналог вместо двух вариантов
W>

А как определить что такое дорогой move конструктор?
В моем представлении если например вектор, который я хочу перекинуть в другое место занимает мегов 500 в памяти, то дорого для него будет копироваться, а move это просто передача указателя на него, как в Си (т.е. ничего нового выделять не нужно, а нужно просто передать 4(8) байт указателя) или опять я упрощаю? ))
т.е. move всегда будет быстрее copy...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.