0

I find myself so frequently thoughtlessly writing classes that add pointers to objects which are later destroyed to lists, which results in a lot of segfaults, but the problem is that sometimes I simply can't come up with a better solution than to store a list of pointers. It does my head in that there doesn't seem to be a simple way of just "storing temporary objects that you can't copy" in C++. Is there a paradigm I'm missing on how to do this effectively?

For example, I'll have a class that looks something like this:

class MyClass
public:
   std::vector<OtherClass *> other_objects;
   void method() {
      OtherObject other_object = create_other_object();
      other_objects.push_back(&other_object);
   }

One reason you may want to do this is because you use the other_objects elsewhere (as arguments to other functions) and you can't copy OtherObject instances (which could happen if the OtherObject copy constructor was private for example).

Of course, once you attempt to use other_objects elsewhere in code, the memory each pointer points to will have already been destroyed.

I write a lot of Python code, and I use this sort of structure very often, where a method populates an iterable with objects that exist solely in the scope of the method. Is there a way to do this effectively in C++? Could I do my own memory management to keep the temporary objects in the method alive beyond the scope of the method?

Matthieu Brucher
  • 21,634
  • 7
  • 38
  • 62
user3002473
  • 4,835
  • 8
  • 35
  • 61
  • 3
    Better not use raw pointers at all but [smart pointers](https://en.cppreference.com/w/cpp/memory) instead. – πάντα ῥεῖ Jan 16 '19 at 17:23
  • I'm not familiar with the concept, I'm only a beginner in C++. Could you share some references or elaborate on how it solves my issue? – user3002473 Jan 16 '19 at 17:24
  • 2
    As I wrote, it takes at least a year to learn these advanced topics. See your C++ book, a year's worth of learning can't really be reduced to a brief answer on stackoverflow. C++ is hard. – Sam Varshavchik Jan 16 '19 at 17:42
  • 2
    @texasbruce: My opinion is that a decade is needed to master C++. A year is certainly not enough. And very few people master C++ (and I don't claim to, even if I know some of it). – Basile Starynkevitch Jan 16 '19 at 17:47

2 Answers2

6

What is the proper paradigm of creating a list of pointers in C++?

Combine std::list (or perhaps some other container) with some smart pointer class (see here) such as std::shared_ptr or std::unique_ptr.

As a rule of thumb (sometimes wrong) avoid raw pointers and prefer smart pointers. Memory management is a tricky topic. If you read some book on garbage collection (e.g. the GC handbook) you'll learn the relevant concepts, terminology and techniques (and these apply even with manual memory management such as reference counting - which some people view as a primitive form of GC). Circular references are painful to handle (be aware of weak references, e.g. std::weak_ptr).

It would take a lot of space and time to explain all this in details (and I don't have time or motivation or even all the skills for that) here. Read a good C++ programming book and refer to some C++ reference (and later perhaps to the C++11 standard n3337 or something newer). Be aware that the memory model of C++ is difficult to understand (for anyone).

Be aware of the rule of five.

On some operating systems and/or C++ implementations, you can find tools, such as valgrind or the address sanitizer of Clang or GCC, which should help you to debug then avoid memory leaks (details are compiler and/or OS specific). And in some few cases (probably a minority of them), you might consider using a garbage collector library (such as Boehm's GC, see this, or MPS).

Is there a way to do this effectively in C++?

I don't know of any efficient, universal and simple way (and Rice's theorem make me believe there cannot be one) to manage memory. Your mileage will vary. You need to precisely understand your memory management and other programming constraints and objectives and make your own trade-offs. See Norvig's opinion. There is No Silver Bullet.


PS. C++ is an extremely difficult programming language. Prepare yourself to spend a lot of time (years, not months; perhaps decades) and efforts to learn it. Look also, for inspiration, into the source code of some well written free software coded in C++. I don't claim to be a C++ master (even if my daily job is about designing and implementing some static source code analyzers for it). I believe that there might be only a few dozens (or perhaps a few hundreds) of C++ masters on this planet, and I am not among them.

PPS. I am biased, but I recommend using Linux on your development machine to learn C++ programming, precisely because it has lots of useful tools.

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
5

No, there is no reason you would ever do this:

  OtherObject other_object = create_other_object();
  other_objects.push_back(&other_object);

This creates a temporary object, and you store the pointer in a list, and then the object is destroyed.

This is the main difference with Python. In Python, every object is a reference to a Python object, even integers. In C++, you have object on the stack (like yours) or on the heap (created through new, make_unique...). If you want to mimick Python behavior, you need objects on the heap.

There are only three valid patterns, depending on what create_object() does:

  • it create an object and returns it by value. In that case, wrap it with make_unique to get a copy. This pattern is usually not very useful and not practical.
  • it create a new object on the heap and expects the calling code to handle memory management. The prototype is thus: std::unique_ptr<OtherClass> create_other_object();
  • it creates a new object but doesn't expect the calling code to handle memory management, in which case the prototype should be OtherClass* create_other_object();

In your case, it's probably the second case:

std::vector<std::unique_ptr<OtherClass>> other_objects;
void method() {
    other_objects.push_back(create_other_object());
}
Matthieu Brucher
  • 21,634
  • 7
  • 38
  • 62