-2

Lets say I have a class Obj, and instances of them are created at run time, can I store them without new like this?

Obj my_obj;

std::vector<Obj> my_vec;

my_vec.push_back(my_obj);

Or do I have to use new?

std::vector<Obj*> my_vec;
my_vec.push_back(new Obj);

And what if I have pointers inside of my class Obj? Also, what if use the second option, do I have to clean up everything?

BOOM
  • 11
  • 2
  • 3
    No, there is no law in C++ that requires `new`. Quite the opposite: one of the most common C++ problems is called "Pointless Use Of Pointers". – Sam Varshavchik Jan 16 '23 at 22:42
  • `vector` is quite happy storing copies of local variables. In the long run this is usually the faster way to go as well as the simpler. Any time you can swing faster and simpler, take it. – user4581301 Jan 16 '23 at 22:43
  • 1
    *"what if I have pointers inside of my class `Obj`?"* - then your `Obj` class should be adhering to the rule of 3/5/0: https://stackoverflow.com/questions/4172722/what-is-the-rule-of-three – UnholySheep Jan 16 '23 at 22:45
  • Yes, in the second example, the clean-up is on you. If you're forced into a structure like this, strongly consider `std::vector>`. It automates the clean-up in every non-fatal condition I can think of. – user4581301 Jan 16 '23 at 22:45
  • @SamVarshavchik But I heard that if I have pointers inside of my class, I should use `new`, is that true? – BOOM Jan 16 '23 at 22:45
  • 1
    No. See comment [three comments above](https://stackoverflow.com/questions/75140346/storing-dicamically-created-objects#comment132598280_75140346). You cannot write effective and efficient C++ code without a firm grasp on the Rule of Three and Friends. – user4581301 Jan 16 '23 at 22:46
  • 1
    No. Whether or not `new` should or should not be used has nothing at all, whatsoever, with the presence or absence of any pointers. It is certainly possible that in some cases this is true, but not in others. There are no absolute rules in C++. One needs to understand, on a fundamental level what pointers are and how they work; and what vectors and other containers are, and how they work, and whether or not the combination of how vectors work, and how pointers work, results in any restriction on how objects get created and used. – Sam Varshavchik Jan 16 '23 at 22:48
  • @BOOM Consider this: `std::vector my_vec; Obj ob; my_vec(&ob);` -- There is no call to `new` there, as the object is not dynamically allocated. So how is your code going to know where that pointer being stored in `my_vec` comes from? It could simply be pointing to a non-dynamically created object. – PaulMcKenzie Jan 16 '23 at 22:54

1 Answers1

5

The question is related to heap allocated objects vs. stack objects. There are already a few good resources on that in Stackoverflow:

(you may want to look at all the above, each has its own angle).

But, your question is specifically about a container holding objects or pointers to objects. For this there are a few other resources:

Let me summarize shortly:

Usually working with values and not pointers should be your preferred option. They are much easier to maintain and less bug-prone. Before C++11 performance considerations could be a reason for considering working with container of pointers, but since move semantics came in C++11, if you design your code properly, working with containers of values should not be costly.

Another reason for working with pointers might be polymorphism. You want your container to hold and manage different types which share the same base class. Though it seems that a container of pointers should be the solution in such a case, which is used in many examples for polymorphism in C++, this still should not be your preferred choice. The first alternative for raw pointers should be holding unique_ptr which is a smart pointer type introduced in the C++ standard library in C++11.

The other option is to model the data that you want to manage withing a class that would hide and manage your polymorphism without exposing it to the container or to the user. Let's take an example: suppose you want to manage a container of Command objects, where Command is a base class for many different actual commands. Instead of having a container of Command* or unique_ptr<Command>, rename Command to be CommandBase, then hold (wrap, hide) unique_ptr<CommandBase> inside class Command and hold in your container objects of type Command.

Another alternative would be to avoid inheritance altogether. So for different callables (our Command example) you can just use std::function.

You can also use std::variant to manage the different types (e.g. using Shape = std::variant<Circle, Triangle>;).

Hiding the fact that you are using polymorphism, or avoiding polymorphism altogether, can become quite useful in your API, with the benefits of being able to preserve proper value semantics.

More on value semantics and its benefits can be found in CppCon 2022 talk by Klaus Iglberger: Back to Basics: Cpp Value Semantics and in C++Now 2022 talk by Dave Abrahams: A Future of Value Semantics and Generic Programming, Part 1 and Part 2. The original talk on the subject, by Sean Parent: Value Semantics and Concepts-based Polymorphism, from C++Now 2012.

You can also read about value semantics in the following links:

Amir Kirsh
  • 12,564
  • 41
  • 74