Funciones y Clases en C++

Recopilación - C/C++(13) 1.Expresiones Lambda07-02 2.Partición y manejo de memoria en C/C++06-13 3.Punteros inteligentes06-14 4.Análisis de punteros complejos en C: regla de derecha a izquierda06-14 5.Cadenas en C/C++06-14 6.Modificadores static, const y extern para variables en C++06-15 7. Funciones y Clases en C++ 06-16 8.Plantillas en C++06-17 9.Preprocesamiento, compilación, enlazado y organización de archivos en C++06-20 10.Manjo de excepciones06-20 11.STL: Iteradores y algoritmos comunes06-23 12.Contenedores STL06-24 13.Flujos en C++07-01 Colapsar Índice

  • I. Definición de funciones
  • II. Paso de parámetros por valor y por referencia
  • III. Constructores y destructores
  • IV. Herencia
  • V. Amistad (friend)

Volver al inicio I. Definición de funciones

En C++, las funciones son unidades básicas para organizar la lógica del código, permitiendo un diseño de programas modular, reutilizable y estructurado.

1. Estructura básica de una función

`tipo_retorno nombre_funcion(lista_parametros) {    // cuerpo de la función    return valor; // opcional, depende del tipo de retorno}`

Declaración (Declaration):

Informa al compilador que la función existe, generalmente en archivos de cabecera:

`int sumar(int a, int b);  // declaración de función`

Definición (Definition):

Proporciona la implementación de la función, generalmente en archivos .cpp:

`int sumar(int a, int b) {    return a + b;}`

2. Sobrecarga de funciones (Overload)

El mismo nombre de función puede tener múltiples definiciones con diferentes parámetros:

`void mostrar(int x);void mostrar(double x);void mostrar(std::string s);`

Nota: La sobrecarga requiere diferencia en el número o tipo de parámetros, el tipo de retorno diferente no basta por sí solo.

3. Parámetros por defecto

`void saludar(std::string nombre = "Invitado") {    std::cout << "Hola, " << nombre << "!\n";}saludar();         // Muestra: Hola, Invitado!saludar("Carlos"); // Muestra: Hola, Carlos!`

4. Funciones en línea (inline)

Sugiere al compilador insertar el código de la función en el lugar de su llamada, ideal para funciones pequeñas y frecuentes.

`inline int cuadrado(int x) {    return x * x;}`

5. Funciones virtuales y puramente virtuales

En C++, las funciones virtuales (virtual) y las funciones puramente virtuales (= 0) son mecanismos clave para implementar el polimorfismo, pero difieren en sintaxis, propósito y comportamiento.

Característica Función virtual (Virtual Function) Función puramente virtual (Pure Virtual Function)
Forma de definición virtual void foo(); virtual void foo() = 0;
Tiene implementación Puede tener implementación (o no) Debe ser implementada en subclases (interfaz abstracta)
Requiere redefinición Subclases opcionalmente pueden redefinirla Subclases deben redefinirla (a menos que también sea abstracta)
Clase contenedora Puede estar en cualquier clase Debe estar en una clase abstracta (contiene funciones puramente virtuales)
Creación de objetos Se pueden instanciar clases con funciones virtuales Clases abstractas no se pueden instanciar
Uso Proporciona implementación por defecto para polimorfismo Fuerza implementación en subclases, como especificación de interfaz

1. Ejemplo de función virtual (puede ser redefinida)

`class Animal {public:    virtual void hablar() {        std::cout << "El animal hace un sonido\n";    }};class Perro : public Animal {public:    void hablar() override {        std::cout << "El perro ladra\n";    }};Animal* p = new Perro();p->hablar();  // Muestra: El perro ladra (polimorfismo)`

Si Perro no redefine hablar(), se usará la implementación por defecto de Animal.

2. Ejemplo de función puramente virtual (debe ser redefinida)

`class Figura {public:    virtual void dibujar() = 0;  // función puramente virtual};class Circulo : public Figura {public:    void dibujar() override {        std::cout << "Dibujando un círculo\n";    }};// Figura f;       // ❌ Error, no se pueden instanciar clases abstractasFigura* p = new Circulo();p->dibujar();        // Muestra: Dibujando un círculo`

Si Circulo no implementa dibujar(), también se volverá una clase abstracta.

Clase interfaz (interface)

C++ no tiene la palabra clave interface, pero puedes simular una interfaz con funciones puramente virtuales:

`class IFlujo {public:    virtual void leer() = 0;    virtual void escribir() = 0;    virtual ~IFlujo() {}  // Las clases interfaz deben tener un destructor virtual};`

6. Expresiones Lambda (C11 en adelante)

`[lista_captura](lista_parametros) -> tipo_retorno {    cuerpo_de_la_funcion}`

Donde:

  • []: lista de captura (puede capturar variables externas)
  • (): lista de parámetros
  • ->: tipo de retorno (puede omitirse)
  • {}: cuerpo de la función

Funciones anónimas, ideales para callbacks concisos:

`auto sumar = [](int a, int b) -> int {    return a + b;};std::cout << sumar(3, 5);  // Muestra: 8// El tipo de retorno puede deducirse, por lo que se puede omitir `-> int`:auto sumar = [](int a, int b) {    return a + b;};std::cout << sumar(3, 4);  // Muestra 7`

Las expresiones Lambda son funciones anónimas introducidas en C++11 para definir objetos funcionales en línea, especialmente adecuadas para escenarios como callbacks en algoritmos, manejo de eventos, creación de hilos, etc.

Captura de variables externas

Método de captura Ejemplo Descripción
Captura por valor [x] Captura el valor de la variable x (copia)
Captura por referencia [&x] Captura la referencia de la variable x
Captura todo por valor [=] Captura el valor de todas las variables externas
Captura todo por referencia [&] Captura la referencia de todas las variables externas
Captura mixta [=, &y] Captura por valor todas excepto y, que se captura por referencia
`int x = 10;int y = 5;auto f = [=, &y]() {    // x se captura por valor, y por referencia    std::cout << x + y << "\n";    y += 1;  // permitido modificar y};f();`

Aplicaciones comunes

  1. Combinación con algoritmos STL (como std::sort)
`std::vector<int> v = {4, 2, 5, 1};std::sort(v.begin(), v.end(), [](int a, int b) {    return a < b;});`

  1. Uso con hilos:
`#include <thread>std::thread t([] {    std::cout << "En un hilo\n";});t.join();`

Lambda mutable

Por defecto, las variables capturadas por valor no pueden modificarse. Con mutable se permiten modificaciones en la copia:

`int x = 5;auto f = [x]() mutable {    x += 1;       // permitido modificar la copia de la variable capturada    std::cout << x;};f();  // Muestra 6, pero x externo no cambia`

Tipos especiales de funciones

Tipo Uso
Constructor Se llama automáticamente al crear un objeto
Destructor Se llama automáticamente al destruir un objeto
Constructor de copia Se llama al inicializar un objeto con otro objeto
Constructor de movimiento Se llama al inicializar un objeto con un valor rvalue
Funciones de sobrecarga de operadores Sobrecargan operadores como +, ==, etc.
Función virtual Para polimorfismo
Función puramente virtual Miembro de función de clase abstracta

Volver al inicio II. Paso de parámetros por valor y por referencia

En C++, los parámetros de función se pasan por valor por defecto, pero no siempre por valor. C++ soporta múltiples formas de paso de parámetros, principalmente:

1. Paso por valor (Pass by Value)

  • Se pasa una copia del argumento real a la función
  • Las modificaciones al parámetro dentro de la función no afectan a la variable original
`void modificar(int x) {    x = 100;}int main() {    int a = 10;    modificar(a);    std::cout << a; // Muestra 10, sin cambios}`

2. Paso por referencia (Pass by Reference)

  • Se pasa un alias de la variable, las modificaciones afectan a la variable original
`void modificar(int& x) {    x = 100;}int main() {    int a = 10;    modificar(a);    std::cout << a; // Muestra 100, modificado}`

3. Paso por puntero (Pass by Pointer)

  • La función recibe la dirección de la variable y accede/modifica a través del puntero
`void modificar(int* x) {    *x = 100;}int main() {    int a = 10;    modificar(&a);    std::cout << a; // Muestra 100}`

4. Paso por referencia constante (Pass by const Reference)

  • Ideal para evitar copias de objetos grandes
  • No permite modificar el argumento
  • Común para pasar objetos como std::string, std::vector
`void mostrar(const std::string& s) {    std::cout << s;}`

5. Paso por referencia a rvalue (Pass by rvalue reference) C11+

  • Soporta semántica de movimiento, evitando copias innecesarias
`void procesar(std::string&& s) {    std::cout << s;}procesar("hola"s);  // paso por movimiento`

Resumen comparativo:

Método ¿Copia? ¿Puede modificar variable original? Caso de uso
Paso por valor No Tipos de datos pequeños (int, float)
Paso por referencia No Cuando se necesita modificar la variable original
Paso por puntero No Similar a referencia, pero más flexible
Paso por referencia constante No No Pasar objetos grandes sin modificar
Paso por referencia a rvalue No Soporta movimiento, evita copias

Conclusión:

En C++, el paso de parámetros es por defecto por valor, pero puedes usar & (referencia), * (puntero) o && (referencia a rvalue) para otros métodos.

Volver al inicio III. Constructores y destructores

Constructor

El constructor es una función especial que se llama automáticamente cuando se crea un objeto, utilizada para inicializar las variables miembro del objeto.

  • Tiene el mismo nombre que la clase
  • No tiene tipo de retorno
  • Puede haber múltiples
Tipo Uso
Constructor por defecto Sin parámetros o todos los parámetros tienen valores por defecto
Constructor con parámetros Proporciona parámetros de inicialización
Constructor de copia Crea un nuevo objeto a partir de uno existente
Constructor de movimiento (C11) "Roba" recursos (más eficiente)
Constructor delegado (C11) Un constructor llama a otro constructor de la misma clase

Constructor de copia

Si no defines explícitamente el operador de asignación, C++ genera automáticamente uno que hace una copia superficial (shallow copy). Pero si tu clase contiene punteros sin gestionar, la copia por defecto causará problemas de copias superficiales (recurso compartido, conflictos en destrucción). En estos casos, debes definir tu propio operador de asignación.

El constructor de copia se usa para:

  • Inicializar un objeto a partir de otro existente
  • Pasar objetos por valor a funciones
  • Devolver objetos por valor desde funciones
`Clase(const Clase& otro);`

El parámetro es const & para evitar llamadas recursivas

Prohibir la copia:

`// En C++11/14/17, se recomienda usar `= default` y `= delete` para especificar explícitamente:class MiClase {public:    MiClase() = default;    MiClase(const MiClase&) = delete;    ~MiClase() = default;};`

Destructor

El destructor es una función especial que se llama automáticamente cuando se destruye un objeto, utilizada para liberar recursos, cerrar archivos, limpiar punteros, etc.

  • El nombre es ~NombreClase
  • No tiene parámetros ni tipo de retorno
  • Cada clase puede tener como máximo un destructor
  • Puede ser virtual (para eliminación polimórfica)

Ejemplo

Implementación completa en un solo archivo de cabecera:

`#include <iostream>#include <string>class Persona {public:    std::string nombre;    int* edad;    // ✅ 1. Constructor por defecto (delega al constructor con parámetros)    Persona() : Persona("desconocido", 0) {        std::cout << "Constructor por defecto llamado (delegado)\n";    }    // ✅ 2. Constructor con parámetros    Persona(const std::string& nombre_, int edad_) {        nombre = nombre_;        edad = new int(edad_);        std::cout << "Constructor con parámetros llamado\n";    }    // ✅ 3. Constructor de copia (copia profunda)    Persona(const Persona& otra) {        nombre = otra.nombre;        edad = new int(*otra.edad);        std::cout << "Constructor de copia llamado\n";    }    // ✅ 4. Constructor de movimiento (C11)    Persona(Persona&& otra) noexcept {        nombre = std::move(otra.nombre); // string tiene su propio movimiento        edad = otra.edad;              // robar puntero        otra.edad = nullptr;          // evitar doble liberación en destructor        std::cout << "Constructor de movimiento llamado\n";    }    // ✅ 5. Destructor    ~Persona() {        std::cout << "Destructor llamado para " << nombre << "\n";        delete edad;    }};`

Escritura moderna recomendada con separación de cabecera y fuente:

`// Persona.h#ifndef PERSONA_H#define PERSONA_H#include <string>class Persona {public:    std::string nombre;    int* edad;	// ✅ 1. Constructor por defecto    Persona();    // ✅ 2. Constructor con parámetros    Persona(const std::string& nombre_, int edad_);    // ✅ 3. Constructor de copia (copia profunda)    Persona(const Persona& otra);    // ✅ 4. Constructor de movimiento (C11)    Persona(Persona&& otra) noexcept;    // ✅ 5. Destructor    virtual ~Persona();    	// Operador de asignación por copia (opcional)    Persona& operator=(const Persona& otra);    // Operador de asignación por movimiento (opcional)    Persona& operator=(Persona&& otra) noexcept;    // ✅ const indica que la función no modificará variables miembro    virtual void presentarse() const;};#endif`

`// Persona.cpp#include "Persona.h"#include <iostream>// ✅ 1. Constructor por defectoPersona::Persona() : Persona("desconocido", 0) {    std::cout << "Constructor por defecto llamado\n";}// ✅ 2. Constructor con parámetrosPersona::Persona(const std::string& nombre_, int edad_) {    nombre = nombre_;    edad = new int(edad_);    std::cout << "Constructor con parámetros llamado\n";}// ✅ 3. Constructor de copia (copia profunda)Persona::Persona(const Persona& otra) {    nombre = otra.nombre;    edad = new int(*otra.edad);    std::cout << "Constructor de copia llamado\n";}// ✅ 4. Constructor de movimiento (C11)Persona::Persona(Persona&& otra) noexcept {    nombre = std::move(otra.nombre);    edad = otra.edad;    otra.edad = nullptr;    std::cout << "Constructor de movimiento llamado\n";}// ✅ 5. DestructorPersona::~Persona() {    std::cout << "Destructor de Persona llamado para " << nombre << "\n";    delete edad;}// Operador de asignación por copia (opcional)Persona& Persona::operator=(const Persona& otra) {    if (this != &otra) {        nombre = otra.nombre;        delete edad;        edad = new int(*otra.edad);    }    return *this;}// Operador de asignación por movimiento (opcional)Persona& Persona::operator=(Persona&& otra) noexcept {    if (this != &otra) {        nombre = std::move(otra.nombre);        delete edad;        edad = otra.edad;        otra.edad = nullptr;    }    return *this;}void Persona::presentarse() const {    std::cout << "Hola, soy " << nombre << ", tengo " << *edad << " años.\n";}`

Resumen del comportamiento por defecto de constructores/copia/destructores

Tipo de función ¿Se genera automáticamente? ¿Cuándo necesita personalización?
Constructor (si no se escribe) Cuando los miembros necesitan inicialización especial
Constructor de copia (si no se escribe) Si contiene punteros, recursos, o si se debe prohibir la copia
Destructor (si no se escribe) Cuando los miembros incluyen recursos dinámicos (como new) deben liberarse
Constructor de movimiento (C11+) Para optimizar o evitar copias
  • Si no lo escribes, el compilador genera automáticamente una versión de "copia superficial".
  • Si la clase tiene punteros sin gestionar, debes escribir tu propio constructor de copia, de lo contrario podrías tener errores de doble liberación.

Copia profunda y copia superficial

En C++, la copia profunda (deep copy) y la copia superficial (shallow copy) son dos formas de copiar objetos, que difieren en si realmente se copian los recursos en el montón. Entender estas diferencias es crucial para dominar los constructores, constructores de copia y destructores de clases.

Definiciones y diferencias

Característica Copia superficial (Shallow Copy) Copia profunda (Deep Copy)
Contenido copiado Solo se copia la dirección del puntero Se asigna nueva memoria y se copia el contenido
Recursos compartidos El objeto original y la copia apuntan a la misma memoria El objeto original y la copia tienen memoria independiente
Seguridad Modificar un objeto afecta al otro; riesgo de punteros colgantes, doble liberación Los objetos no se afectan entre sí
Riesgo en destrucción Doble liberación de la misma memoria (si no se gestiona correctamente) Cada objeto libera su propia memoria

Ejemplo demostrativo

`#include <iostream>#include <cstring>class Persona {public:    char* nombre;    // Constructor    Persona(const char* n) {        nombre = new char[strlen(n) + 1];        strcpy(nombre, n);    }    // Constructor de copia superficial (por defecto)    // Persona(const Persona& otra) = default;    // ✅ Constructor de copia profunda    Persona(const Persona& otra) {        nombre = new char[strlen(otra.nombre) + 1];        strcpy(nombre, otra.nombre);    }    // Destructor    ~Persona() {        delete[] nombre;    }    void imprimir() {        std::cout << "Nombre: " << nombre << std::endl;    }};`

Si no escribes el constructor de copia profunda, el compilador usa por defecto la copia superficial, es decir, solo copia la dirección del puntero nombre. Esto significa que ambos objetos apuntan a la misma memoria en el montón. Así, modificar p2.nombre afectaría a p1.nombre, y ambos objetos al destruirse intentaría liberar la misma memoria varias veces, provocando un fallo.

Comportamiento por defecto en copia

Tipo de operación Comportamiento por defecto ¿Es seguro?
Constructor de copia Copia superficial
Operador de asignación = Asignación superficial
Destructor Liberación por defecto (si se usan punteros sin gestionar)

El rol de const en las clases

const puede modificar funciones miembro de la clase, parámetros y valores de retorno de funciones, y los objetos, pero no puede modificar directamente toda la clase.

const modifica funciones (funciones miembro constantes)

`class MiClase {public:    int obtenerValor() const; // ✅ indica que la función no modificará variables miembroprivate:    int valor = 42;};int MiClase::obtenerValor() const {    // this->valor = 10; ❌ Error de compilación: no se pueden modificar variables miembro    return valor;}`

`const MiClase obj;   // ✅ **solo puede llamar a funciones miembro constantes**MiClase obj2;        // ✅ **puede llamar tanto a funciones miembro constantes como no constantes**`

const modifica parámetros y valores de retorno

Modificación de parámetros

`void imprimirNombre(const std::string& nombre); // ✅ evita copia + garantiza que no se modifique el parámetro`

Modificación de valores de retorno

`const std::string& obtenerNombre() const; // ✅ el valor de retorno no puede ser modificado (previene uso incorrecto)`

Nota: Cuando const modifica valores de retorno, generalmente se usa para devolver referencias o punteros, rara vez para devolver por valor.

No se puede modificar directamente la clase

`const class MiClase {}; // ❌ poco común, básicamente sin sentido`

Volver al inicio IV. Herencia

Las funciones de la clase base se modifican con virtual, y las subclases pueden usar override para redefinirlas

Modificadores de herencia y visibilidad

La herencia en C++ es idéntica a la de Java, con los mismos modificadores de herencia:

Modificador de herencia Los miembros public de la base se convierten en Los miembros protected de la base se convierten en Los miembros private de la base
public public protected No accesibles
protected protected protected No accesibles
private private private No accesibles

Visibilidad

Modificador Acceso dentro de la clase Acceso en clases derivadas Acceso fuera de la clase
public Accesible Accesible Accesible
protected Accesible Accesible No accesible
private Accesible No accesible No accesible

Ejemplo

`//estudiante.h#ifndef ESTUDIANTE_H#define ESTUDIANTE_H#include "Persona.h" class Estudiante : public Persona {public:    std::string escuela;    Estudiante();    Estudiante(const std::string& nombre_, int edad_, const std::string& escuela_);    ~Estudiante() override;    void presentarse() const override;};#endif`

`//estudiante.cpp#include "Estudiante.h"#include <iostream>Estudiante::Estudiante() : Persona("estudiante_desconocido", 18), escuela("Escuela Desconocida") {    std::cout << "Constructor por defecto de Estudiante\n";}Estudiante::Estudiante(const std::string& nombre_, int edad_, const std::string& escuela_)    : Persona(nombre_, edad_), escuela(escuela_) {    std::cout << "Constructor con parámetros de Estudiante\n";}Estudiante::~Estudiante() {    std::cout << "Destructor de Estudiante llamado para " << nombre << "\n";}void Estudiante::presentarse() const {    std::cout << "Hola, soy el estudiante " << nombre << ", tengo " << *edad              << " años y estudio en " << escuela << ".\n";}`

Orden de constructores y destructores

  • Orden de construcción: primero la clase base → luego la clase derivada
  • Orden de destrucción: primero la clase derivada → luego la clase base
`class Base {public:    Base() { std::cout << "Base()\n"; }    ~Base() { std::cout << "~Base()\n"; }};class Derivada : public Base {public:    Derivada() { std::cout << "Derivada()\n"; }    ~Derivada() { std::cout << "~Derivada()\n"; }};`

Orden de salida:

Base()
Derivada()
~Derivada()
~Base()


Característica específica de C++: herencia múltiple y herencia virtual - Problema de la herencia en diamante

Herencia múltiple

`class A { public: int x; };class B { public: int x; };class C : public A, public B {};  // herencia múltiple, C tiene dos x, debe distinguir A::x y B::x`

El "problema de la herencia en diamante (Diamond Problem)" es un conflicto estructural específico en la herencia múltiple de C++ que causa:

  1. Duplicación de subobjetos de la clase base
  2. Ambigüedad en el acceso a datos / acceso ambiguo
  3. Confusión en construcción y destrucción
  4. Desperdicio de recursos

¿Qué es la estructura de herencia en diamante?

Como se muestra en el diagrama, B y C heredan de A, y D hereda de B y C:

`      A     / \    B   C     \ /      Dclass A {public:    int valor;};class B : public A {};class C : public A {};class D : public B, public C {};`

Problemas causados por la herencia en diamante

1. Se crean múltiples subobjetos A

En D hay dos objetos A:

  • Uno de B (B→A)
  • Uno de C (C→A)
2. Ambigüedad al acceder a valor
`D d;d.valor = 10; // ❌ Error: el compilador no sabe si es B::A::valor o C::A::valor`

Se debe especificar manualmente:

`d.B::valor = 10;d.C::valor = 20;`

Solución: herencia virtual (virtual)

Usa virtual para modificar la relación de herencia, haciendo que B y C compartan el mismo subobjeto A.

`class A {public:    int valor;};class B : virtual public A {};class C : virtual public A {};class D : public B, public C {};`

Efecto de la herencia virtual:
  • En D solo hay un objeto A, coordinado automáticamente por el compilador
  • El acceso a miembros ya no es ambiguo:
`D d;d.valor = 10; // ✅ Acceso normal, sin ambigüedad`

Volver al inicio V. Amistad (friend)

En C++, el mecanismo de amistad (friend) permite que funciones no miembro u otras clases accedan a los miembros privados (private) y protegidos (protected) de una clase. La relación de amistad es unidireccional y no transitiva, y se usa comúnmente para sobrecargar operadores, herramientas de depuración o colaboración estrecha entre clases.

I. Tres formas de amistad

1. Función amiga (Friend Function)

`class Caja {private:    int ancho;public:    Caja(int w) : ancho(w) {}    // Declarar función amiga    friend void mostrarAncho(const Caja& c);};// Función no miembro, pero puede acceder a miembros privados de Cajavoid mostrarAncho(const Caja& c) {    std::cout << "Ancho: " << c.ancho << std::endl;}`

Características:

  • No es una función miembro de la clase, pero tiene acceso a sus miembros privados
  • Se usa comúnmente para sobrecargar operadores como operator<<

2. Clase amiga (Friend Class)

`class Motor;class Coche {private:    int velocidad;public:    Coche(int s) : velocidad(s) {}    friend class Motor;  // Motor puede acceder a miembros privados de Coche};class Motor {public:    void mostrarVelocidad(const Coche& c) {        std::cout << "Velocidad: " << c.velocidad << std::endl;    }};`

Características:

  • Una clase puede declarar otra clase como amiga
  • Todas las funciones miembro de la clase amiga pueden acceder a los miembros privados de la clase que declara la amistad
  • Unidireccional: Motor es amigo de Coche, pero Coche no puede acceder a miembros privados de Motor

3. Función miembro como amiga

`class B;class A {private:    int a_valor;public:    A(int v) : a_valor(v) {}    friend void B::mostrarA(const A& a);  // solo una función miembro específica de B se convierte en amiga};class B {public:    void mostrarA(const A& a) {        std::cout << a.a_valor << std::endl;    }};`

Nota: Este método requiere declarar primero la clase B, de lo contrario el compilador no sabrá qué es B::mostrarA.

II. Resumen de características de amistad

Característica Descripción
Acceso unidireccional La clase declarada como amiga puede acceder a miembros privados de la clase que declara, pero no al revés
Funciones no miembro también pueden ser amigas Funciones ordinarias, clases, funciones miembro de clases pueden ser declaradas como amigas
No rompe la encapsulación Aunque pueden acceder a miembros privados, el alcance de acceso está explícitamente limitado
Enlace en tiempo de compilación La relación de amistad se establece en tiempo de compilación, no puede establecerse dinámicamente en tiempo de ejecución
No se hereda ni transmite Las subclases no heredan los derechos de amistad, y las clases amigas de la clase amiga tampoco tienen acceso

III. Ejemplo: sobrecarga del operador <<

`class Persona {private:    std::string nombre;    int edad;public:    Persona(std::string n, int a) : nombre(n), edad(a) {}    friend std::ostream& operator<<(std::ostream& os, const Persona& p);};std::ostream& operator<<(std::ostream& os, const Persona& p) {    os << "Nombre: " << p.nombre << ", Edad: " << p.edad;    return os;}`

Autor original: aslanvon Publicado en: https://www.cnblogs.com/aslanvon/p/18931148

Etiquetas: C++ Programación Orientada a Objetos funciones clases herencia

Publicado el 8-3 03:19