When should I choose whether or not to use a pointer when creating an object?

Asked

Viewed 1,393 times

25

In C++, I’m used to seeing objects being created through the operator new, which is when the object is referenced by a pointer, thus:

MinhaClasse *mc1 = new MinhaClasse();

This form seems to me the most natural to create an object because it reminds me of Java, where everything is pointers except for primitive types (int, char, byte, etc...).

However, in C++ there is the possibility of instantiating an object without creating a pointer, thus:

MinhaClasse mc2;

Are there differences in the creation of objects using these two ways? If so, what would they be? When each should be chosen over the other?

  • I think for large objects that will be passed to other objects is used with pointer. Already the creation MinhaClasse mc2; requires the passage by copy.

2 answers

23


The difference is precisely whether to use the object as a value or as a reference. This is important because it determines where object data will be allocated.

The second way usually puts in the stack. Although I could put in the heap also if the object is being allocated in a type that is in the heap (if the declaration is placed directly on a container).

The shape with the operator new will allocate memory in the heap and store the object there (the new is the allocator, the MinhaClasse() is the initializer). The value used for this variable will be a pointer (indirect), therefore it is considered a value by reference. This pointer which is the value to be used in this data is usually placed in the stack - example case - but can also be placed as a member of another object in the heap. Note that the pointer and the contents of the object are in the heap does not mean that they are together, on the contrary. They are independent things that have a relationship because a reference to another.

C++ lets the consumer decide where they want to allocate. This is different from Java for example, where all objects are allocated in heap through pointers - except the primitive types, as pointed out in the question. The philosophy of C++ is not to facilitate the use but to facilitate the best use of computational resources. The consumer has more information than is best at the time.

Lifespan

Life time usually determines more how allocation is done. Even if you have a relatively large object to be allocated and the programmer knows that he will only live within that scope or he understands well and the scope and he can be easily determined, that there is no risk of recursive calls or other functions allocating other large objects, it is possible to allocate in the stack no pointer. It is not common practice, it is difficult to do right and only compensates in cases of extreme optimization.

It is possible to create pointers to the stack also, without the use of the new. This is also unusual. It is more common to use a reference for this, which is a pointer below the scenes, but you don’t need to know this and the control is better, although it can give some problems in the current version of C++ if the life time is shorter than expected (There is forecast for future releases not to let this happen in the same line as Rust does and does not let use wrong lifespan).

Object size

In cases where the object is large the copy will cost expensive, so it is better to store in a location that the life time can be indeterminate and access this information through an indirect (a pointer). This is usually less efficient, so the use of reference is more adopted.

In general when the object has a certain size and is small, it does not make sense to use the direct pointer (reference yes). Even if the die has a lifetime beyond the function where it is being used (see how the stack) or needs to be used outside an object where it is declared, the copy of the data that may be required in these cases can cost very cheap.

Of course some objects can be large, then usually consider the worst case. There is a situation where the consumer has more information. A type may have been created considering that the object may be large but in a specific use case it has a certain size and is small. In Java this would not be considered. In C++ a good programmer chooses to use the allocation inline, possibly in stack.

But small objects can be used by reference when it is known that they have a certain lifespan yet not so obvious.

Indeterminating the size of the object may be another good reason to prefer the use of pointer. Even if you know that the object will be small but cannot determine its exact size, how do you linearize it within another object? Creating a pointer that has fixed size referencing the actual object that has variable size.

Exchange is not so free

There are cases where the type was created thinking of a form of allocation and use otherwise will probably bring problems.

It is common to use some standard other than documentation to inform which is the preferred form of allocation. You can use struct for cases where you should allocate the direct value and class when the preference is to use an indirect. Some languages such as C# and D, and in a certain way Java, if you consider that primitive types act as structures (and that future Java will probably have the programmer create his own), determine the form of allocation depending on how the type was declared. To C++ struct and class are almost synonymous (the only difference is the visibility default of its members) and does not determine the allocation. At most developers are recommended to adopt this convention.

In general changeable types are better when allocated through pointers or references, otherwise a phenomenon called slicing.

A pointer is an indirect, which gives some flexibility.

Pointer management

Remembering that in most cases, in C++, one should use the smart pointers and not raw pointers. Thus you gain automatic memory management. If there is no internal mechanism in the class itself or the allocation system (new can be overloaded and can even allocate on a Garbage Collector, although unusual and impractical today in C++) the programmer has to ensure that the delete correspondent be executed.

Some cases the type indicates a semantics and controls the use of pointers on its own. String is an example of a class that internalizes the pointer. It is a case that in general has no reason to allocate a pointer because the data that the program has direct access is small, the large and variable part of the text will be better allocated by the class, possibly using a pointer (it can optimize small cases and not do this, depends on implementation).

Completion

The choice should always be to allocate in stack - which is relatively small and basically fixed - until there is a reason to choose the heap, and the main reason is that the data must survive at the end of a function and be potentially too large to be copied. A secondary reason is potentially too big to fit in stack or make an object of indeterminate size or too large.

  • 5

    And that, kids, is what happens when you save to answer the question after lunch and have a mustache running around. Very good, haha.

  • 1

    Complementing a little, the allocation process with the new constuma is costly and consumes a few clock cycles, while the stack allocation is practically free, it is usually just updating a register. This is important, especially for the people who come from Java, who use the new in all that is part and then complain that the program in c++ is slow.

9

Using pointers, you create references,

Example:

#include <iostream>

//Classe de exemplo
class Objeto{

public:
//Inicializa o valor como 0 por padrão na criação de objetos da classe
    Objeto(){

        this -> valor = 0;
    }

    int valor;      
};

int main()
{
     //Referência
    Objeto *a = new Objeto();   

    Objeto *b = a;  //cria o objeto b que aponta para a

    a -> valor = 5; //muda o valor de a. Também muda o de b, pois b aponta para a.

    std::cout << "a: " << a -> valor << "\n"
              << "b: " << b -> valor << std::endl;

    //Valor
    Objeto c;
    Objeto d = c; //Cria o objeto d, que será cópia de c

    c.valor = 8; // Não muda o valor de d que será mantido como inicializado (0)

    std::cout << "c: " << c.valor << "\n"
              << "d: " << d.valor << std::endl;

    return 0;
}

Exit:

a: 5
b: 5
c: 8
d: 0

a and b point to the same object, that is, in fact only one object was created, but two variables access it. Ja d is only a copy of c. The two will follow their own path after creation. Choose the first option when you want to modify data in one object by means of "another". To have more data security and avoid that one object modifies the data of another uses copies.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.