operators &, * in c ++ and its use in classes

4

In the method within the object class ... what is the difference between

int getEdad(){
    return edad;
}

int& getEdad(){
    return edad;
}


int* getEdad(){
    return &edad;
}

Any more detailed course or explanation of pointers but referring to classes in c ++?

    
asked by Mario Josue Grieco Villamizar 18.12.2016 в 22:50
source

3 answers

5
int getEdad(){ return edad; }

This version returns a copy of edad . This copy will be totally independent of the original variable and will have its own life cycle.

int& getEdad(){ return edad; }

int* getEdad(){ return &edad; }

In these two versions you return a reference of edad (in the form of reference and pointer). The main difference with the first version is that from outside the version you could get to modify the member variable edad .

Now then. What are the implications of using one method or another? Answering this question is not trivial. Each option has its advantages and disadvantages:

Worth

The main advantage of this solution is that it maintains the encapsulation intact. It will be impossible to modify the member variable edad from the return value, which prevents, for example, incorrect values:

int a = getEdad();
a = -1; // edad no cambia de valor

The main disadvantage is found when returning complex objects. Returning a copy of an object involves copying its entire state (including the state of the nested objects), which consumes execution time.

By reference

By returning the value by reference we offer the possibility of avoiding unnecessary copies, which can improve the execution times (that and that there are objects that the copy constructor may have disabled).

The toll to pay is that we are exposing the member variable and that may allow you to be assigned invalid values:

int& a = getEdad();
a = -1; // edad valdrá -1

However, that the function returns a reference does not imply that we have to take advantage of this feature. So we can make a copy without problems:

int a = getEdad(); // creamos una copia
int& b = getEdad(); // creamos una referencia

Its main use is usually found when returning complex member objects, since we allow to modify its properties without having to duplicate its interface:

escena.getConfiguracion().setColorBase(0x224466);

per pointer

This solution is very similar to the previous one.

I would highlight two significant differences:

  • Creating a copy of the object implies a slightly uglier code:

    int a = *getEdad();
    
  • the pointers can create confusion on whether the one that obtains the pointer has to make the corresponding delete:

    int* a = *getEdad();
    delete a; // es lo correcto?
    

Its main use should be to return objects that the receiver should eliminate, to avoid creating confusion.

As a final note comment that the second way is not usually found in old code, the most common being the first and the third.

    
answered by 19.12.2016 в 00:29
2

The ampersand is playing the role of reference. If you know the role of a pointer, the reference is a similar concept.

This may sound like pointers, where you can return the memory address of a variable and then by dereferencing it modify the value of the original variable. For example, third function ( int* getEdad() , which returns a pointer):

// Pongamos que inicialmente la edad es 18.
int* fuera = getEdad();
*fuera = 20;

// Esto imprimirá 20.
std::cout << *(getEdad()) << std::endl;

With references you can do the same. Let's take the example but now with the second function ( int& getEdad() ):

// De nuevo la edad inicial es 18.
int& fuera = getEdad();
fuera = 20;

// Esto también imprime 20.
std::cout << getEdad() << std::endl;

If the pointers already exist, why do we want the references if they seem to do the same? Because the references differ from the pointers in two things:

  • A pointer you have to explicitly dereference if you want to modify its content, as we do in the first block of code with the asterisks in the assignment and then doing the cout . With a C ++ reference, this is implicit.
  • Since the references are not pointers, they can not host the NULL value, so it is understood that they will always have a safe value, avoiding doing type variable == NULL checks.
  • answered by 19.12.2016 в 00:01
    0
      

    Any more detailed course or explanation of pointers but referring to classes in c ++?

    There are no differences in the use of pointers with basic data or with objects. In both cases the pointers work in the same way: they contain a memory address which can be (or not be) the memory address of a data.

    Basic notions.

    The functions that you have set, as long as they belong to a 1 object, would return respectively:

  • int getEdad() { return edad; } copy of edad .
  • int& getEdad() { return edad; } reference to edad .
  • int* getEdad() { return &edad; } pointer to edad .
  • These three styles of returning data from a function have elementary differences.

    • In the first case you get a copy of the internal data and any modification on it will not affect the original data; On the other hand, this copy may have a life cycle different from the original data.
    • In the second case you get a reference to the internal data , this allows you to make changes to the reference that will be reflected in the original data; but if the original data is deleted you will have a hanging reference and operate on this reference will cause unexpected behavior in your program ... this reason (among others) is considered a bad practice to return a reference to an internal data.
    • In the third case you get a pointer to the internal data , through the pointer you could modify the original data so it has the same disadvantages as the reference.

    Broadly speaking, the difference between pointer and reference to an internal data is that once the reference is created it can not point to another data while the pointer does. On the other hand, a pointer can be null while a reference can not.

    Intermediate notions.

    Returning a reference does not imply that you are going to have a reference. In other words: the return type of a function does not have to be the type used to store the return; Suppose we have this class:

    struct S
    {
        int edad{};
    
        int  copia()      { return edad;  }
        int& referencia() { return edad;  }
        int* puntero()    { return &edad; }
    };
    

    We can save a reference to the returned data in all cases:

    S s;
    const int& referencia1 = s.copia();
    int& referencia2 = s.referencia();
    int& referencia3 = *s.puntero();
    

    Both referencia2 and referencia3 would be a reference to s.edad and any operation on one of those references would be reflected in both the other and the original data.

    Regarding referencia1 would be a reference to a temporary data ( S::copia returns one data per copy) and C ++ does not allow this type of references unless they are constant. A constant reference to a temporary data extends the life cycle of the data until the end of the reference life cycle, otherwise we would have a hanging reference.

    In the same way we can save a pointer to the returned data:

    S s;
    int* puntero1 = &s.copia();
    int* puntero2 = &s.referencia();
    int* puntero3 = s.puntero();
    

    Both puntero2 and puntero3 contain the memory address of s.edad and s.edad could be modified through these pointers.

    The case of puntero1 is a compilation error: it is not possible to obtain the memory address of a temporary value

    Finally we can the data per copy regardless of what the function returns:

    S s;
    int dato1 = s.copia();
    int dato2 = s.referencia();
    int dato3 = *s.puntero();
    

    In all three cases, including the dato2 , we will have a copy of s.edad .

    Advanced notions.

    The et ( & ) can be used in another context in the declaration of a member function:

    struct S
    {
        int edad{};
    
        int  copia() &&      { std::cout << "c&&\n"; return edad;  }
        int& referencia() && { std::cout << "r&&\n"; return edad;  }
        int* puntero() &&    { std::cout << "p&&\n"; return &edad; }
    
        int  copia() &      { std::cout << "c&\n"; return edad;  }
        int& referencia() & { std::cout << "r&\n"; return edad;  }
        int* puntero() &    { std::cout << "p&\n"; return &edad; }
    };
    

    The version with an et ( & ) corresponds to the calls of the member function when the owner object is not a temporary object. The version with two et ( && ) is for when the member function is called from a temporary object:

    S s{};
    
    auto a = s.copia();      // muestra c&
    auto b = s.referencia(); // muestra r&
    auto c = s.puntero();    // muestra p&
    
    auto d = S{}.copia();      // muestra c&&
    auto e = S{}.referencia(); // muestra r&&
    auto f = S{}.puntero();    // muestra p&&
    

    Keep in mind that in the previous example e is a hanging reference and f is a pointer to data that no longer exists, because they obtained references or pointers to temporary data.

    1 And each one had a different name, since you can not overload it by return ... well yes it can (with a bit of witchcraft) but it's not what you have to talk about this question:)

        
    answered by 20.12.2016 в 17:26