Член класса C ++: распределение стека и кучи - PullRequest
0 голосов
/ 24 сентября 2018

Рассмотрим класс, подобный

class Element{
    public:
    // approx. size of ~ 2000 Byte
    BigStruct aLargeMember;

    // some method, which does not depend on aLargeMember
    void someMethod();
}

Теперь предположим, что многие экземпляры Element (например, 100 000 000 экземпляров во время выполнения, с одновременным существованием около 50 000 экземпляров) Element создаются во время выполнения, и очень часто толькоsomeMethod() вызывается без необходимости выделения памяти для aLargeMember.(Этот иллюстративный пример получен из нелинейного кода конечных элементов, класс Element фактически представляет конечный элемент.)

Теперь мой вопрос: поскольку aLargeMember не требуется очень часто, и учитывая большое количествоэкземпляры Element, было бы выгодно создавать aLargeMember динамически?Например,

class Element{
    public:
    // approx. size of ~ 2000 Byte
    std::unique_ptr<BigStruct> aLargeMember;

    // only called when aLargeMember is needed
    void initializeALargeMember{
        aLargeMember = std::unique_ptr<BigStruct>( new BigStruct() );}

    // some method, which does not depend on aLargeMember
    void someMethod();
}

По сути, это соответствует рекомендации 4, приведенной в https://stackoverflow.com/a/36646563/4859499:

Использовать новое только в случае явной необходимости, например:

  • особенно большое выделение, которое потребляет большую часть стека (ваша ОС / процесс будет иметь «согласованный» лимит, обычно в диапазоне 1-8 + мегабайт)

    • , еслиэто единственная причина, по которой вы используете динамическое распределение, и вы хотите, чтобы время жизни объекта было связано с областью действия в вашей функции, вы должны использовать локальный std :: unique_ptr <> для управления динамической памятью и убедиться, что этоосвобождается независимо от того, как вы покидаете область действия: путем возврата, выброса, прерывания и т. д. (Вы также можете использовать элемент данных std :: unique_ptr <> в классе / структуре для управления любой памятью, которой владеет объект.)

Итак, мой вопрос: считается ли подход кучи в данном случае плохой практикой?Или есть какие-либо хорошие аргументы против кучи в настоящем деле?Заранее спасибо!

1 Ответ

0 голосов
/ 24 сентября 2018

Член класса C ++: стек против размещения кучи

Не путайте распределение на основе стека с наличием переменных-членов объектов, выделенных из кучи.Если у вас есть объект этого класса, выделенный в куче:

class Element{
    public:
    // approx. size of ~ 2000 Byte
    BigStruct aLargeMember;

    // some method, which does not depend on aLargeMember
    void someMethod();
}

, тогда в стеке 1009 * ничего не выделяется.

Поскольку aLargeMember не требуется оченьчасто, учитывая большое количество экземпляров Element, было бы выгодно динамически создавать aLargeMember?

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

Считается ли подход кучи в данном случае плохой практикой?

Этот вопрос является слишком общим и слишком легко может быть истолкован как основанный на мнении, но в контексте данного фрагмента кажется, что вы имеете в виду сценарий распределения переменной-члена по необходимости.для использования на основе, и, таким образом, может быть предпочтительна ленивая инициализация для уменьшения объеманеобходимость вручную поддерживать инициализацию для каждой точки кода, требующей этого.Для этого вы можете обернуть доступ к этому члену, чтобы убедиться, что вы возвращаете что-то инициализированное.Просто для иллюстрации, очень не поточнобезопасный, идея заключается в следующем:

class Element{
private:
    // approx. size of ~ 2000 Byte
    std::unique_ptr<BigStruct> aLargeMember;

    // A wrapper through which you access aLargeMember
    BigStruct& GetLargeMember()
    {
        if (!aLargeMember)
        {
            aLargeMember = std::make_unique<BigStruct>();
        }

        return *aLargeMember;
    }

public:
    // some method, which might depend on aLargeMember
    void someMethod();
};

Если вам нужно передать его для использования за пределами области действия класса, то вы будете в опасности повиснутьссылка, поскольку выделенный экземпляр, владеющий unique_ptr, может быть уже уничтожен.Если это так, и вы действительно хотите гарантировать от этого, то unique_ptr не подходит, так как вы можете только переместить .Вместо этого рассмотрите возможность использования shared_ptr и верните фактический умный указатель из GetLargeMember().

Или есть ли в данном случае хорошие аргументы против кучи?

Не против использования кучи здесь, но есть как минимум несколько шаблонов, которые вы можете использовать для своего распоряжения.Например, учитывая, что вы намереваетесь создать такое большое количество экземпляров, но гораздо реже существующих одновременно, я бы серьезно рассмотрел объединение экземпляров class Element.

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