Why Should I Use a Pointer Rather Than the Object Itself

Why should I use a pointer rather than the object itself?

It's very unfortunate that you see dynamic allocation so often. That just shows how many bad C++ programmers there are.

In a sense, you have two questions bundled up into one. The first is when should we use dynamic allocation (using new)? The second is when should we use pointers?

The important take-home message is that you should always use the appropriate tool for the job. In almost all situations, there is something more appropriate and safer than performing manual dynamic allocation and/or using raw pointers.

Dynamic allocation

In your question, you've demonstrated two ways of creating an object. The main difference is the storage duration of the object. When doing Object myObject; within a block, the object is created with automatic storage duration, which means it will be destroyed automatically when it goes out of scope. When you do new Object(), the object has dynamic storage duration, which means it stays alive until you explicitly delete it. You should only use dynamic storage duration when you need it.
That is, you should always prefer creating objects with automatic storage duration when you can.

The main two situations in which you might require dynamic allocation:

  1. You need the object to outlive the current scope - that specific object at that specific memory location, not a copy of it. If you're okay with copying/moving the object (most of the time you should be), you should prefer an automatic object.
  2. You need to allocate a lot of memory, which may easily fill up the stack. It would be nice if we didn't have to concern ourselves with this (most of the time you shouldn't have to), as it's really outside the purview of C++, but unfortunately, we have to deal with the reality of the systems we're developing for.

When you do absolutely require dynamic allocation, you should encapsulate it in a smart pointer or some other type that performs RAII (like the standard containers). Smart pointers provide ownership semantics of dynamically allocated objects. Take a look at std::unique_ptr and std::shared_ptr, for example. If you use them appropriately, you can almost entirely avoid performing your own memory management (see the Rule of Zero).

Pointers

However, there are other more general uses for raw pointers beyond dynamic allocation, but most have alternatives that you should prefer. As before, always prefer the alternatives unless you really need pointers.

  1. You need reference semantics. Sometimes you want to pass an object using a pointer (regardless of how it was allocated) because you want the function to which you're passing it to have access that that specific object (not a copy of it). However, in most situations, you should prefer reference types to pointers, because this is specifically what they're designed for. Note this is not necessarily about extending the lifetime of the object beyond the current scope, as in situation 1 above. As before, if you're okay with passing a copy of the object, you don't need reference semantics.

  2. You need polymorphism. You can only call functions polymorphically (that is, according to the dynamic type of an object) through a pointer or reference to the object. If that's the behavior you need, then you need to use pointers or references. Again, references should be preferred.

  3. You want to represent that an object is optional by allowing a nullptr to be passed when the object is being omitted. If it's an argument, you should prefer to use default arguments or function overloads. Otherwise, you should preferably use a type that encapsulates this behavior, such as std::optional (introduced in C++17 - with earlier C++ standards, use boost::optional).

  4. You want to decouple compilation units to improve compilation time. The useful property of a pointer is that you only require a forward declaration of the pointed-to type (to actually use the object, you'll need a definition). This allows you to decouple parts of your compilation process, which may significantly improve compilation time. See the Pimpl idiom.

  5. You need to interface with a C library or a C-style library. At this point, you're forced to use raw pointers. The best thing you can do is make sure you only let your raw pointers loose at the last possible moment. You can get a raw pointer from a smart pointer, for example, by using its get member function. If a library performs some allocation for you which it expects you to deallocate via a handle, you can often wrap the handle up in a smart pointer with a custom deleter that will deallocate the object appropriately.

When to use pointers, and when not to use them

For the do's and dont's of C++:

Effective C++ and More Effective C++ by Scott Meyers.

For pointers (and references):

  • use pass by value if the type fits into 4 Bytes and don't want to have it changed after the return of the call.
  • use pass by reference to const if the type is larger and you don't want to have it changed after the return of the call.
  • use pass by reference if the parameter can't be NULL
  • use a pointer otherwise.

dont't use raw pointers if you don't need to. Most of the time, a smart pointer (see Boost) is the better option.

C++ pointer vs object

T* represents a pointer type, which represents a variable that contains a "reference" (usually a memory address) to some instance of type T. Using a real world comparison, a T* pointer stands to T like a street address stands to a building.

Pointers allow you to refer to some instance owned by some other variable, and you can use a valid, non null instance of T* to read and write on a T. In this, they are similar to another C++ concept, references (written as T&), which allow you to alias variables, but differ significantly from pointers by not being objects in their own regard.

A pointer is, in fact, an object itself, with each pointer variable having its own unique address and being thus storable and referenceable. For instance, you can have pointers to pointers (T**) and references to pointers (T*&), but not pointers to references - pointers exist, while references may not (they are usually implemented as pointers underneath though).

To reflect the this "indirect" nature of pointers, C and C++ provide you with two different operators which allow you to dereference a pointer (* and ->), and to reference a variable (&).

For instance, you may do the following:

struct A { int x; };

// ...

A a {};
A *aptr { &a }; // `&` takes the address of `a` and stores it into the `aptr` variable of type `A*`

aptr->x = 33; // `->` is equivalent here to `(*aptr).x`, a.x is now 33

A a2 {};
A **aptrptr { &aptr }; // pointer to pointer

*aptrptr = &a2; // `aptr` now points to `a2`

operator-> is basically syntactic sugar that avoids you some cumbersome expressions like (*aptr).x.

References, being basically just aliases to something else, do not need any special syntax at all, and are always converted transparently when neeeded:

int x { 33 };
int &xref { x }; // `xref` refers to `x`
xref = 12; // `x` is now 33

int y = xref; // copies `x` into `y`, no special syntax needed

Pointers are also used in the C language to access arrays, which always decay to a pointer as soon as they are referred to in expressions. This is messy and it's one of the reasons std::vector and std::array should always be used in their place when feasible.

int x[33];
x[3] = 44; // equivalent to `*(&x[0] + 3) = 44`

Finally, the "indirect" nature of pointers and references allow C++ to convert a Derived* to a Base*, given that a derived class contains a full instance of its base (it gets more complicated with multiple inheritance though).

Every class that inherits or contains from another class containing virtual methods will include a hidden pointer to a _Virtual Method Table`, a list of pointers to functions which will be used to dispatch the virtual methods to the correct implementation.

PS: in modern C++, you should rarely need raw pointers. The correct approach is to use containers such as std::vector, std::array and std::string, and special pointer-like wrappers called smart pointers (like std::unique_ptr) every time you need a pointer for some reason. These will handle the lifetime of a pointer for you, avoiding memory leaks and vastly simplifying memory handling. For the same reason, the new operator should be mostly considered as being deprecated and should not be used at all (unless in placement new expressions, are potentially dangerous if used improperly).

C++ Objects: When should I use pointer or reference

A reference is basically a pointer with restrictions (has to be bound on creation, can't be rebound/null). If it makes sense for your code to use these restrictions, then using a reference instead of a pointer allows the compiler to warn you about accidentally violating them.

It's a lot like the const qualifier: the language could exist without it, it's just there as a bonus feature of sorts that makes it easier to develop safe code.

Is it better to use a pointer instead of returning a value?

Side effects should be avoided if not absolutely needed. If you can return the value - return it. Basically, the function should a black-box which does something with parameters and returns the value.

Why:

  1. It is much safer. You definitely will not invoke undefined behaviour.
  2. It helps the compiler to optimize the code.

In your example -

int example_return(void)
{
return (1);
}

in C++, what's the difference between an object and a pointer to an object?

It's exactly as you said.

When you pass an object by value, its copy constructor is invoked to produce a new instance of such object that will be used inside the function. The changes done to such new object won't be reflected to the original one1.

As with structures, the default copy constructor just does a shallow copy of the original object - i.e., its fields are copied2 to the new instance; in many cases this is not desirable (e.g. if the object wraps a pointer/another resource), so there are classes which redefine the copy constructor or disable it completely. Objects of these last classes can only be passed by pointer or reference.

Passing objects by value can be costly if they are bigger than a pointer (in size) or in general if their copy constructor isn't "cheap". On the other hand, in comparison to pointers, the pass-by-value yields the usual advantages of not having to specify the pointer ownership, letting the callee do whatever it wants with the object, etc.

Notice that passing an object by value kills the polymorphism. This because a function receiving an object by value receives a statically typed object, with a precise size and type, so any attempt to pass an object of a derived class will result in object slicing (the copy constructor for the base class is called, that by default just copies the fields that are available in the base class).

This is the reason why often the preferred method of passing objects is by const reference. This yields several advantages:

  • no copies involved; the object that the callee will see will be exactly the one specified at the moment of the call;
  • no changes to the original object can be made, thanks to the const qualifier;
  • if however the callee needs to change a copy of the object, it can still construct a copy by itself from the reference;
  • no awkward pointer syntax;
  • polymorphism preserved, since behind the scenes we're actually passing a pointer;
  • no big doubts about object ownership: the general rule about references is that they are owned by the caller.



  1. As far as the "raw fields" of the object are concerned; naturally if the original object and the copy continue to share a pointer/handle to the same resource some modifications to one may affect the other.

  2. Primitive types (and in general PODs) are copied bitwise, while the copy constructor is called for non-POD types.

Why use references rather than pointers?

I'm not making a point to use one or the other - I'm asking why are references used over pointers to save data members?

Both pointers and references are used as data members. Reference T& is as flexible as immutable pointer T* const. Additionally reference may not be null. So if those properties are describing what you need in the best way then use reference.

IOW, if every Person must have a House and Person may not switch to other House during Person's lifetime then use reference. Apparently both premises feel ridiculous so pointer is actually likely better.

Pointer is not always better. Favor immutability (int* const) to mutability (int*) when things stay immutable. Favor certainty (exactly one) to uncertainty (one or none) when things are certain.

In OOP the composition relation in component object is usually done as reference to composite object. A component can not exist without composite and can not become component of other composite so reference is best.

Pointer to temporary object [duplicate]

Both codes have undefined behavior.

Here:

{
A a;
ptr = &a;
}

std::cout << ptr->value << endl; // "test"

ptr becomes invalid once a goes out of scope and gets destroyed.

Similar in the second example, you are also dereferencing an invalid pointer because the temporary string is gone after the call to the constructor.

C++ does not define what happes when you do things that are not defined (makes sense, no? ;). Instead it is rather explicit about saying that when you do certain wrong things then the behavior of the code is undefined. Dereferencing an invalid pointer is undefined. The output of the code could be anything. A compiler is not required to diagnose undefined behavior, though there are features and tools that can help. With gcc it is -fsanitize=address that detects the issue in both your codes: https://godbolt.org/z/osc9ah1jo and https://godbolt.org/z/334qaKzb9.



Related Topics



Leave a reply



Submit