Должен ли я хранить полностью инкапсулированный элемент по ссылке, по значению или по ptr? - PullRequest
2 голосов
/ 02 марта 2012

Итак, у меня есть стандартная установка C ++ с объектом, который хранит другой объект.Хранимый объект принадлежит полностью, он никогда не просачивается наружу.Элемент неконстантный.

class Container
{
private:
    Contained item;
}

Как я понимаю, когда создается экземпляр моего контейнера, для элемента item вызывается конструктор по умолчанию, и мне не нужно управлять им в списке инициализатора.,Также правильно ли я понимаю, что когда мой объект будет уничтожен, dtor на item будет вызываться автоматически?

Другой вариант - сохранить его по ссылке, конечно,

class Container
{

private:
    Contained& item;

public:

    Container() : Contained()
    {

    }
}

в этом случае я не знаю, должен ли я delete это в dtor.

Еще один вариант - сохранить его по ptr

class Container
{
private:
    Contained* item;
public:
    Container()
    {
        item = new Contained();
    }

    ~Container()
    {
        delete item;
    }
}

Зная, что мой item никогдавозвращается к вызывающей стороне и никогда не попадает во внешний API и никогда не переназначается, каков наилучший способ продолжить?Как я уже говорил, элемент item не является константой (это будет структура данных с самоизменяющимся размером).

Ответы [ 5 ]

2 голосов
/ 02 марта 2012

Самое простое - хранить сам объект. Использование ссылки для этой цели, я бы сказал, сбивает с толку. Одним из преимуществ использования указателя является то, что вы можете избежать определения типа Contained в заголовочном файле - вместо этого вы можете переслать объявление Contained и сохранить все детали в файле .cpp.

1 голос
/ 02 марта 2012

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

1 голос
/ 02 марта 2012

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

0 голосов
/ 02 марта 2012

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

В таком случае, имея его в качестве переменной-членаимеет смысл.

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

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

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

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

Поэтому, если вы не можете использовать unique_ptr, который разрешает прямое объявление, я бы выбрал необработанный указатель и не-copyable class.

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

0 голосов
/ 02 марта 2012

В отличие от Лучиана Григоре, я бы выбрал метод указатель / ссылка: сохранение инкапсулированного объекта в качестве ссылки или указателей позволяет вам объявить его вперед, что экономит время компиляции.

В дополнение к этому он позволяет вам иметь функции-члены init() и destroy(), которые в свою очередь будут вызывать конструктор и деструкторы инкапсулированного объекта, а также выполнять инициализацию других частей объекта. Таким образом, неправильная инициализация может быть обработана возвращаемым значением init().

...