Этикет C ++ о переменных-членах в куче - PullRequest
9 голосов
/ 27 февраля 2009

Считается ли это плохими манерами / плохой практикой явно помещать элементы объекта в кучу (через new)? Я думаю, вы можете позволить клиенту выбрать область памяти для создания экземпляра объекта. Я знаю, что может быть ситуация, когда куча членов может быть приемлемой. Если вы знаете ситуацию, не могли бы вы описать ее, пожалуйста?

Ответы [ 4 ]

13 голосов
/ 27 февраля 2009

Если у вас есть класс, предназначенный для семантики копирования, и вы выделяете / освобождаете кучу памяти без необходимости, я могу посчитать это плохой практикой. В общем, правда, это не так. Есть много классов, которые могут использовать кучу хранилища. Просто убедитесь, что у вас нет утечек памяти (освободите объекты в деструкторе, счетчиках ссылок и т. Д.), И все в порядке.

Если вы хотите большей гибкости, попробуйте указать вашему пользователю Allocator . Я объясню.

Некоторые классы, например std::vector, строка, карта и т. Д. нужно хранилище кучи для структур данных, которые они представляют. Это не считается плохими манерами; если у вас автоматически выделен vector, пользователь должен знать, что буфер выделяется при вызове конструктора vector:

void foo() {
    // user of vector knows a buffer that can hold at least 10 ints
    // gets allocated here.
    std::vector<int> foo(10);  
}

Аналогично, для std::string вы знаете, что есть внутреннее выделение кучи char*. Есть ли по одному на string экземпляр, как правило, зависит от реализации STL; часто они подсчитывают ссылки.

Тем не менее, почти для всех классов STL пользователи do могут выбирать, куда их поместить, в том смысле, что они могут указать распределитель. vector определяется примерно так:

template <typename T, typename Alloc = DefaultAllocator<T> >
class vector {
    // etc.
};

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

vector<int, MyCustomAllocator> foo(10);

Теперь, когда конструктор выделяет, он будет использовать MyCustomAllocator вместо значения по умолчанию. Вот некоторые подробности по написанию собственного распределителя STL .

Если вы беспокоитесь о том, что использование кучи для определенного хранилища в вашем классе может быть «нехорошим способом», вы можете рассмотреть возможность предоставления пользователям вашего класса такой опции, чтобы они могли указать, как распределяться, если ваша стратегия по умолчанию не соответствует их потребностям.

10 голосов
/ 27 февраля 2009

Я не считаю это плохой практикой вообще. Существует множество причин, по которым вы можете явно выделить переменную-член через new. Вот некоторые из моих голов.

  • Скажем, у вашего класса очень большой буфер, например, 512 КБ или 1 МБ. Если этот буфер не хранится в куче, ваши пользователи могут потенциально превысить пространство стека по умолчанию, если они создадут несколько локальных переменных вашего класса. В этом случае имеет смысл выделить буфер в конструкторе и сохранить его как указатель.
  • Если вы осуществляете подсчет ссылок любого рода, вам понадобится указатель, чтобы отслеживать, сколько объектов на самом деле указывают на ваши данные.
  • Если ваша переменная-член имеет время жизни, отличное от вашего класса, указатель - путь. Прекрасным примером этого является ленивая оценка, когда вы платите только за создание элемента , если пользователь запрашивает его.
  • Хотя это не обязательно является прямой выгодой для ваших пользователей, время компиляции является еще одной причиной использования указателей вместо объектов. Если вы помещаете объект в свой класс, вы должны включить файл заголовка, который определяет объект в файле заголовка для вашего класса. Если вы используете указатель, вы можете заранее объявить класс и включить только заголовочный файл, который определяет класс в исходных файлах, которые в нем нуждаются. В больших проектах использование предварительных объявлений может значительно сократить время компиляции за счет уменьшения общего размера ваших блоков компиляции.

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

1 голос
/ 27 февраля 2009

Где класс помещает своих членов менее важно, чем то, что управление ими содержится внутри класса; т.е. клиентам и подклассам не нужно беспокоиться о переменной-члене объекта.

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

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

0 голосов
/ 27 февраля 2009

хм, я не очень понимаю ваш вопрос.

Если у вас есть класс:

class MyOtherClass;
class MyClass
{
  MyOtherClass* m_pStruct;
};

Тогда клиент MyClass не имеет реального выбора того, как будет выделяться m_pStruct.

Но решение о том, как сам класс MyClass будет размещен в стеке или в куче, будет принимать решение клиента:

MyClass* pMyClass = new MyClass;

или

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