Menú

Mostrar Mensajes

Esta sección te permite ver todos los mensajes escritos por este usuario. Ten en cuenta que sólo puedes ver los mensajes escritos en zonas a las que tienes acceso en este momento.

Mostrar Mensajes Menú

Mensajes - Loretz

#101
Usando regex:

#include <iostream>
#include <string>
#include <regex>

int main()
{
    std::string frase{ "Galapagar, pasar y no parar; y de parar, pagar, pagar y callar." };

    std::regex buscar("\\bpagar\\b");

    std::sregex_iterator ppio(frase.begin(), frase.end(), buscar);
    std::sregex_iterator fin = std::sregex_iterator();

    std::cout << "Encontrada(s): "
        << std::distance(ppio, fin)
        << " palabra(s) completa(s):\n";

    while (ppio != fin) {
        std::smatch match = *ppio;
        std::cout << match.str() << "\n";
        ppio++;
    }

}
#102
La sobrecarga del operador << suele hacerse de dos maneras (típicamente); si necesita acceder a miembros privados o protegidos de la clase, será una función friend; si no lo necesita, se prefiere una función libre. (Más, te diría que siempre que existe la alternativa, se prefieren funciones libres a funciones miembro).

No quisiera entrar en controversias personales, pero... sobre la afirmación de YreX-DwX:
CitarAmbas sobrecargas son funciones, no métodos.

Es muy frecuente usar la palabra "método" para referirse a funciones miembro de alguna clase, pero que sea un uso común no quiere decir que sea la forma correcta, y menos que sea obligatorio. El estándar C++ las llama "member function", y los que no hablamos inglés, por comodidad les decimos "funciones miembro", claro que también alguien puede decirles "procedimientos", "métodos", o cosas así, pero es sólo por comodidad, no porque así deba decirse. (Prueba: ponte a buscar las distintas ocurrencias de la palabra "method" en el documento del estándar y verás que en ningún caso se aplica a funciones, a ningún tipo).

Por otro lado:
void Persona::InsertarNombre(const char * _nuevoNombre){
     strcpy(nombre, _nuevoNombre);
}

Aquí tienes un problema, "nombre" no tiene memoria asignada (con new, por ejemplo), es un puntero que apunta a quién sabe donde, de modo que lo esperable es que strcpy haga un destrozo.

#103
Citartengo que imaginar al delete como un interruptor que desactiva el espacio de memoria y al new como que lo activa..
Casí que es así, no vas a encontrar "activar" pero sí "reclamar", e incluso "allocar" (puaj), la palabra apropiada en el lenguaje C++ es el verbo inglés "allocate".

Si te fijas en la documentación (por ejemplo, en un sitio que no es el del comité estándar pero que todo el mundo tiene a mano: https://en.cppreference.com/w/cpp/memory/new/operator_delete
Verás que el operator delete es toda una familia de funciones, dedicadas a liberar (deallocate) la memoria que se haya pedido con su correspondiente new.

Como ves, delete necesita el nombre de una variable para saber qué liberar, si no hay variable no habrá delete. Por ejemplo, una forma de no liberar nunca memoria asignada (capturada, allocated) con new puede ser:
void f() { new int; } Aquí new int; reserva un espacio sizeof(int) en la memoria libre (digamos... 4 bytes), que jamás podrá liberar ningún delete, porque ni nombre tiene el pobre.

Haciendo algo más razonable:
void g() { int* i = new int; }
Ahora, al salir de g(), la variable i va a morir (es una variable local que termina su vida acotada a su ámbito), pero una copia temporal de esa i queda para ser devuelta como un temporal por la llamada a g().

Por eso es que tiene sentido hacer:
int* p = g();
porque ahora p va a tener el valor de ese temporal que devolvió g().

Si se hubiera hecho sólo:
g(); una llamada a g() sin asignar su valor devuelto a ninguna variable de tipo int*, se hubieran perdido esos 4 bytes irremediablemente.

pero después de
int* i = new int;
se podría hacer perfectamente
int* j = i;
int* k = j;

de este modo los tres punteros apuntan al mismo espacio de memoria. E invocando un delete en cualquiera de ellos se liberarán esos 4 bytes tomados por new. Puedes hacer, por ejemplo:
delete k;

Lo que no puedes hacer (bueno, puedes, pero el sistema operativo sabrá tratarte como te mereces) es invocar a delete en más de uno de esos, porque estarías tratando de devolver dos o tres veces la memoria que se reclamó una sola vez. Un new y tres deletes, no te atrevas.

Como este tipo de errores siempre fue frecuente y catastrófico, el C++ actual evita el uso de new / delete, y sanseacabó. unique_ptr y shared_ptr son tus amigos.







#104
CitarCuando retorna un valor puntero o un valor comun y silvestre no se pierde el valor retornado
?
- Tanto para valores comunes como para punteros: si el valor retornado se lo asignas a alguna variable no se perderá, si no se lo asignas a nadie se perderá.

Citar... entonoces qué pasa con la variable declarada dentro de la funcion?.. no se destruye?...
- Sí, en todos los casos se destruye, no importa qué tipo de variable sea.

Lo que pasa es que las funciones (que devuelven algo), retienen una copia temporal del valor devuelto. Sería estos pasos:
int f() { int i=5; return i; }

  - al llegar a } se hace una copia de i para ser devuelta, y luego muere i.
  - al invocar a f() se dispone (efímeramente) de ese valor temporal devuelto.

(esto no es realmente así porque el C++ exige que los compiladores implementen lo que se llama "return value optimization", pero eso nosotros no lo vemos, es un truco del compilador).

Una variable puntero (un int* o un cosa*) tiene una vida local, como cualquier otra variable, pero la dirección de memoria accedida con new (o malloc, o todos sus parientes), es eterna, se libera con delete (o free) por el programador, o se libera al sistema cuando termina el programa.

Cuando haces:
int j = f();

j en este caso va a tener el valor devuelto por f(), que es ese temporal.

Lo mismo cuando una variable es un puntero, igual.

Si ese temporal devuelto no se lo asignas a nadie, (una variable o una referencia), adiós.

Moraleja: un puntero es una variable más, da lo mismo tener un char que un char*, que un mySuperObjeto*; la diferencia es el tipo de "valor" que se les puede asignar. A una variable se le asigna un valor, un 5, por ejemplo; a un puntero se le asigna una dirección de memoria donde hay un 5. Salvo sutilezas, un puntero es una variable más y se comporta como una.

#105
Creo que a mediados del sigo XX solía verse funciones que creaban un puntero para ser liberados después, por la función que hacía la llamada. Mira, te pongo un ejemplo:

Citarint* RetornaUnaSuma(int a, int b)
{       

    int * suma = new int; // IMPORTANTE !!! NO OLVIDARSE DE LIBERAR LA MEMORIA CON delete
                          // INSISTO, ES MUY IMPORTANTE, Y NO VAYAIS A USAR free, debe ser delete !!
    *suma = a + b;
    return suma;
}

int main() {
    int a = 1;
    int b = 2;

    int* suma = RetornaUnaSuma(a, b);

    std::cout << "*suma == " << *suma << '\n';

    // ah! y como soy muy aplicado y leo los comentarios de todas las funciones que uso:
    delete suma;
}

#106
Cuando pasas un puntero no constante como parámetro de una función, se sobreentiende que la intención es cambiar el valor de lo apuntado, igual que con las referencias.

En tu caso, la función podría ser:
void RetornaUnaSuma(int* pSuma, int a, int b)
{
    *pSuma = a + b;
}


Donde pSuma se pasa como puntero no constante, y cualquiera que ve su declaración (en un .h por ejemplo), asume que la idea de la función es modificar eso a lo que apunta.

Su uso sería:
int main()
{
    int a = 1;
    int b = 2;
    int* pSuma = new int;

    RetornaUnaSuma(pSuma, a, b);

    std::cout << "*pSuma == " << *pSuma << '\n';

    // y por supuesto:
    delete pSuma;

}


Como ves, que se devuelva un puntero a int no está mal, sólo que no es necesario.

Aunque no puedo dejar de mencionar que en C++ no se usa new / delete, casi nunca.
#107
Cita de: MAFUS en 10 Febrero 2019, 19:40 PM
Por lo que veo C++ ha cambiado mucho; pero en C, un lenguaje mucho más fiel a sus orígenes sí son necesarios los punteros pues el tratamiento de la memoria sigue estando en manos del programador.

Por otra parte si te alejas del modelo de memoria protegido de los uP modernos y sus sistemas operativos y programas para modos reales, uC y estas historias, puedes acceder directamente a diferentes zonas de memoria; así puedes cambiar datos directamente en la memoria de vídeo, acceder a los datos que te proporcionan los sensores y periféricos mapeados en memoria y hacer diferentes cosas que los lenguajes sin punteros no pueden hacer.

MAFUS; el C++ usa punteros, es el programador de C++ el que ya casi no usa más "punteros crudos", punteros del estilo C.

string, vector, list, unique_ptr, referencias, ... en el fondo son (tienen) punteros, sólo que se usan de manera más segura y eficiente.

No importa cuántos archivos haya en el proyecto, cuántos programadores han estado ahí durante años, en C++ un memory leaks es una rara avis. Y es el programador quien decide el uso de la memoria libre, cuándo tomarla, cuando liberarla, eso siempre fue así. Y con las referencias a rvalues viene también la semántica de move.

Por ejemplo, aprovechando que se mencionó más arriba; nadie usaría qsort en C++, la std::sort de C++ pude ser 10 veces más rápida para hacer lo mismo (no es posible exagerar el énfasis, 10 veces más rápida). https://www.geeksforgeeks.org/c-qsort-vs-c-sort/

#108
El uso de punteros en C++ es algo muy excepcional, en los últimos 10 años ya casi nadie en el mundo real usa punteros.

No se usa new, (no se usa delete) se usan smart pointers (std::unique_ptr y std::shared_ptr).

Es más, siempre que se puede elegir entre una variable en la stack (pila, variables de duración automática) o en memoria libre, probablemente la mejor opción sea la stack.

En las funciones de tu ejemplo, en la primera deberías haber pasado una copia del numero; en la segunda y la tercera deberías haber pasado una referencia.

void consultarNumero(int _numero) {
   cout << "El numero es : " << _numero << endl;
}

void modificarNumero(int& _numero, int _nuevoNumero) {
   _numero = _nuevoNumero;
}

void sumarUnoAlNumero(int& _numero) {
   _numero++;
}

int main() {
   int numero = 5;

   consultarNumero(numero);
   modificarNumero(numero, 6);
   sumarUnoAlNumero(numero);
}




Un caso común en el que suele preferirse un puntero es cuando se necesita interactuar con alguna librería en C, o pasar como parámetro el puntero crudo de un smart pointer, sin transferir el ownership y sabiendo que el tiempo de vida del objeto está manejado por quién invoca la función. También podría pasarse una referencia, pero si el parámetro puede ser nulo no irá una referencia, irá un puntero. Ejemplo:

struct ObjetoMuyPesado {
    static inline const string BibliotecaDeAlejandria { "Empezando por el catálogo ... " };
    size_t size() const { return BibliotecaDeAlejandria.max_size(); }
};

void cuantosBytes(ObjetoMuyPesado* biblio) {
    if (!biblio)
        throw std::runtime_error("quemada");

    std::cout << "La biblioteca de Alejadnria tiene "
        << biblio->size() << " bytes\n";
}

int main() {
    auto biblio = std::make_unique<ObjetoMuyPesado>();
    cuantosBytes(biblio.get());
}


Cuando eres un principiante te esfuerzas por entender y aprender a usar punteros, es natural, supongo. Cuando ya crees que has aprendido, te sientes orgulloso y quieres mostrar el mundo que sabes usar punteros. Cuando has aprendido algo no usas punteros.

#109
Una variante, un poco más simple, creo:


int pedir_numero()
{
    cout << "Introduce una opcion: ";
    string data{};

    while (getline(cin, data))
    {
        if (data.length() != 1 || !isdigit(data[0])) // isdigit() sabe si es 0...9
        {
            // cerr se prefiere para mensaje de error que suelen ir a auditoría,
            // quizá aquí mejor cout.
            // y no uses endl, si realmente lo necesitas es porque hay algo muy raro
            // en tu sistema.
            cout << "Numero invalido, vuelve a intentarlo!\n";
            continue;
        }

        // si llegó hasta aquí es un número de un solo dígito,
       
        return data[0] - '0';

    }

    return -1;
}


#110
Hay un programa muy famoso, que no me acuerdo ahora, pero debe tener algo más de 30 años, que se llamaba "eliza", que actuaba como psicóloga. Bueno, no engañaba a nadie, pero era muy gracioso.
Desde entonces se reprodujo muchas veces, puedes buscar "eliza en C++" y cosas así.
Un ejemplo:
http://www.martinbroadhurst.com/eliza-in-c.html