Encapsulación y modificadores de acceso en C++ (public, protected, private)
Aprende encapsulación y modificadores de acceso en C++ con public, protected y private para diseñar clases seguras.
Encapsulación es uno de los cuatro conceptos fundamentales de la Programación Orientada a Objetos (POO). Consiste en ocultar los datos (atributos) de una clase del mundo exterior y permitir el acceso a ellos solo a través de métodos definidos. Este enfoque protege la integridad de los datos y hace que la estructura interna de una clase sea independiente del uso externo.
1) ¿Qué es la encapsulación?
La encapsulación es el principio de “ocultamiento de datos”. Los detalles internos de una clase (atributos, métodos auxiliares) están ocultos al exterior. El usuario interactúa únicamente mediante la interfaz pública de la clase.
Ventajas de la encapsulación:
- Los datos están protegidos contra el mal uso o la modificación indebida.
- La lógica interna de la clase puede cambiar sin afectar el código externo.
- Aumenta la seguridad, la mantenibilidad y la capacidad de prueba.
2) Modificadores de acceso
En C++, existen tres tipos de modificadores de acceso:
| Modificador | Nivel de acceso | Uso |
|---|---|---|
public |
Accesible desde cualquier parte | Interfaz pública de la clase (API) |
protected |
Accesible solo dentro de la clase y las clases derivadas | Comportamientos comunes utilizados en la herencia |
private |
Accesible solo dentro de la clase | Campos de datos, funciones auxiliares |
3) Ejemplo de encapsulación con la clase Book
A continuación, un ejemplo de una clase Book que demuestra el principio de encapsulación.
#include <iostream>
#include <string>
using namespace std;
class Book {
private:
string title;
string author;
double price;
int pageCount;
public:
// Constructor
Book(string t, string a, double p, int pc)
: title(t), author(a), price(p), pageCount(pc) {}
// Getters y Setters
string GetTitle() const { return title; }
string GetAuthor() const { return author; }
double GetPrice() const { return price; }
void SetPrice(double p) {
if (p >= 0) price = p;
else cout << "¡El precio no puede ser negativo!" << endl;
}
int GetPageCount() const { return pageCount; }
void Print() const {
cout << "Libro: " << title
<< " | Autor: " << author
<< " | Páginas: " << pageCount
<< " | Precio: " << price << " €" << endl;
}
};
int main() {
Book b("C++ Primer", "Lippman", 180.0, 950);
b.Print();
b.SetPrice(-50); // Inválido
b.SetPrice(210);
b.Print();
}
Los campos private no pueden modificarse directamente.
Las modificaciones deben hacerse a través de métodos controlados como SetPrice().
4) Campos protegidos y herencia
Las clases derivadas pueden acceder directamente a los campos protected.
Esto permite que la subclase amplíe o reutilice los datos de la clase base.
class EBook : public Book {
protected:
string format; // Ejemplo: PDF, EPUB
public:
EBook(string t, string a, double p, int pc, string f)
: Book(t, a, p, pc), format(f) {}
void PrintWithFormat() const {
cout << GetTitle() << " (" << format << ")" << endl;
}
};
Aquí, la clase derivada añade una nueva propiedad (format) sin violar la encapsulación.
5) La palabra clave friend
Normalmente, los miembros private solo son accesibles dentro de la clase.
Sin embargo, a veces otra clase o función necesita acceder a esos datos privados.
En tales casos, se puede usar la declaración friend.
class Book {
private:
string title;
double price;
public:
Book(string t, double p) : title(t), price(p) {}
// Declaración de función amiga
friend void ApplyDiscount(Book& b, double rate);
};
void ApplyDiscount(Book& b, double rate) {
b.price *= (1 - rate); // acceso al miembro privado
}
int main() {
Book b("Design Patterns", 300);
ApplyDiscount(b, 0.1);
}
El uso de friend debe hacerse con precaución, ya que rompe la encapsulación.
Solo debe usarse cuando sea necesario (por ejemplo: sobrecarga de operadores o herramientas de depuración).
6) Principios de diseño de getters y setters
- Use getters en línea (
const) para accesos simples a datos. - Incluya siempre validación dentro de los setters.
- Haga los campos solo de lectura (solo getter) si no deben modificarse.
- En lugar de definir getters/setters innecesarios, abstraiga el comportamiento directamente mediante métodos.
class Book {
private:
string title;
double price;
public:
Book(string t, double p) : title(t), price(p) {}
string GetTitle() const { return title; } // solo lectura
void ApplyDiscount(double rate) { price *= (1 - rate); } // comportamiento en lugar de setter
};
7) Seguridad de datos con la palabra clave const
Los métodos const no modifican el estado de la clase.
Esto apoya la encapsulación y evita modificaciones inesperadas.
class SafeBook {
private:
string title;
public:
SafeBook(string t) : title(t) {}
string GetTitle() const { return title; } // método const
void SetTitle(string t) { title = t; }
};
Un método Get escrito sin const viola este principio y puede causar errores inesperados.
8) Patrón de objeto inmutable (Immutable)
De acuerdo con el principio de encapsulación, algunos objetos se diseñan para ser “inmutables”.
Todos sus miembros se declaran const y no tienen métodos setter.
class ImmutableBook {
private:
const string title;
const string author;
const int pages;
public:
ImmutableBook(string t, string a, int p)
: title(t), author(a), pages(p) {}
string GetTitle() const { return title; }
string GetAuthor() const { return author; }
int GetPages() const { return pages; }
};
Este enfoque garantiza la integridad de los datos, especialmente en entornos multihilo.
9) Encapsulación y gestión del estado del objeto
La encapsulación no solo controla el acceso, sino que también garantiza que el objeto permanezca en un estado válido. El estado interno de la clase siempre debe tener valores coherentes.
class ValidatedBook {
private:
double price;
public:
void SetPrice(double p) {
if (p < 0) throw invalid_argument("El precio no puede ser negativo.");
price = p;
}
};
Estos mecanismos de validación garantizan que la clase mantenga un “estado válido” y evitan que el código externo proporcione datos incorrectos.
10) TL;DR
- La encapsulación consiste en ocultar los datos y acceder a ellos a través de un comportamiento controlado.
private: accesible solo dentro de la clase.protected: accesible dentro de la clase y sus subclases.public: accesible desde cualquier lugar (interfaz de la clase).friendpermite acceso externo a datos privados (úsese con precaución).- La validación y el uso de
consten getters/setters son fundamentales. - Los objetos inmutables aumentan la seguridad de los datos.
- La encapsulación es un principio esencial para diseños mantenibles y sin errores.
- Todos los ejemplos pueden ejecutarse en Visual Studio 2022 o GCC 11+.