Допустим, у меня есть глобальный набор, определенный следующим образом:
std::set<Person> people;
У меня также есть функция, в которой я создаю экземпляр класса Person и добавляю его в набор следующим образом:
void addEntry() {
Person p1("kosh", 55);
people.emplace(p1);
}
Насколько я понимаю, будет создана копия объекта (я вижу, как вызывается конструктор копирования) и добавлена в набор. Как только эта функция завершается, p1 выходит за пределы области действия. Но каков объем копии, добавляемой в набор? Это определяется объемом набора?
РЕДАКТИРОВАТЬ 1:
Последующий вопрос:
В таком случае лучше просто создать p1 в куче, а не в стеке, и сохранить указатель в наборе?
вы почти никогда не захотите хранить указатели в стандартных контейнерах. Указатели нужны для хранения полиморфных объектов и в некоторых других случаях, но не здесь.
Будет ли что-то «лучше» всегда зависит от ваших потребностей. Наборы даже не перемещают свои элементы, поэтому некоторые причины хранить указатели (по какой-то причине дорогостоящие перемещения или желание сохранить постоянные указатели this) неприменимы. Тем не менее, std::unordered_map<std::string, std::unique_ptr<ItemBase>> children; — здесь мы ссылаемся на базовый класс, который может быть разными реальными классами. std::unordered_map<PackageID, std::shared_ptr<const LicenseInfo>> licenses; -- здесь объекты могут сохраняться за пределами их присутствия на карте.
«область действия» применяется к переменным. Что касается ценностей, вопрос должен стоять о «сроке жизни».





Но каков объем копии, добавляемой в набор? Это определяется объемом набора?
Да. Скопированный объект хранится внутри объекта set и будет уничтожен при очистке или уничтожении set. Если только вы не erase() заранее введете копию.
В таком случае лучше просто создать
p1в куче, а не в стеке, и сохранить указатель в наборе?
Хотя это позволит избежать копирования, вам придется иметь дело с накладными расходами на динамическое выделение и управление временем жизни объекта, на который указывает. Кроме того, хранение указателей в виде set в любом случае противоречит цели использования set, поскольку по умолчанию он не сможет сравнивать объекты, а будет сравнивать только указатели. Вам придется предоставить set собственный компаратор, который разыменовывает указатели.
Более простым вариантом было бы просто не передавать объект в emplace() для его копирования. Вы можете передать нужные параметры конструктора Person непосредственно в emplace(), и он создаст для вас объект Person, например:
void addEntry() {
people.emplace("kosh", 55);
}
Время жизни объекта — это отрезок времени во время выполнения программы, в течение которого объект активен и к нему можно получить доступ.
Область действия — это места в коде, из которых может быть осуществлен такой доступ. Смотрите также:
Область действия и время жизни переменной
Вы вызываете метод emplace() набора со ссылкой на локальную переменную.
Что делает emplace(t)? Его функция довольно одинакова для разных коллекций:
emplace(t): Создайте новый объект внутри контейнера как копию объекта t.
(I've simplified things bit, e.g. w.r.t. move semantics)
Итак, вы на самом деле не кладете t внутрь контейнера. Вы создаете другой объект; а контейнеры, такие как std::set(), реализованы так, что время жизни созданного элемента будет фактически таким же, как и у контейнера.
Примечания:
std::set предназначен для упорядоченных элементов; математическое понятие набора лучше отражается с помощью std::unordered_set.лучше просто создать
p1в куче, а не в стеке, и сохранить указатель в наборе?
Почему это было бы лучше? ... Я полагаю, вы бы сказали, что избегайте создания двух Person.
Ну, а вообще, попробуйте что-нибудь другое: вообще не создавайте p1; просто emplace() используя аргументы конструктора Person класса, и тогда никогда не будет никакого объекта, кроме того, который создан внутри набора. Таким образом, создается и затем используется только один экземпляр Person.
Пожалуйста, не задавайте несколько вопросов в одном сообщении. Если у вас есть еще дополнительные вопросы, задайте их отдельно.