Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Ramiro Torres
Semestre 2021-B
2
Contents
3
4 CONTENTS
Bibliografı́a
5
6 CONTENTS
Chapter 3
3.1 Punteros
El valor de cada variable está almacenado en un lugar determinado de la memoria, caracterizado
por una dirección (que se suele expresar con un número hexadecimal). Gracias a los nombres de
las variables, el programador no se preocupe de la dirección de memoria donde están almacenados
sus datos. Sin embargo, en ciertas ocasiones es más útil trabajar con las direcciones que con los
propios nombres de las variables para incrementar la velocidad de los programas. El manejo de
direcciones se lo realiza mediante los punteros o apuntadores.
Ası́, un puntero es una variable que puede contener unicamente la dirección de otra variable
y se dice que un puntero apunta a una variable si su contenido es la dirección de dicha variable.
Un puntero se debe declarar de acuerdo con el tipo del dato al que apunta bajo la siguiente
sintaxis:
tipo_dato * nombre_puntero;
Ejemplo 1 Un puntero a una variable de tipo int se declara del siguiente modo:
int *direc;
• Los valores posibles para un puntero son las direcciones posibles de memoria.
7
8 CHAPTER 3. CONCEPTOS BÁSICOS DE C++
3.2 Arreglos
3.2.1 Arreglos Estáticos
Un array es un modo de manejar una gran cantidad de datos del mismo tipo bajo un mismo
nombre o identificador. Por ejemplo, mediante la sentencia:
double a[5];
se reserva espacio para 5 variables de tipo double. Las 5 variables se llaman a y se accede al valor
de cada una de ellas por medio de un subı́ndice. Un subı́ndice es una expresión entera escrita a
continuación del nombre entre corchetes [...]. La forma general de la declaración de un vector es
la siguiente:
tipo nombre[n];
Los elementos se numeran desde 0 hasta (n − 1). El tamaño de un vector puede definirse con
cualquier expresión constante entera.
Ejemplo 3
double a[10];
a[5] = 0.8;
a[9] = 30. * a[5];
a[0] = 3. * a[9] - a[5]/a[9];
a[3] = (a[0] + a[9])/a[3];
Las matrices se declaran de forma análoga, con corchetes independientes para cada subı́ndice.
La forma general de la declaración es:
tipo nombre[numero_filas][numero_columnas];
donde tanto las filas como las columnas se numeran también a partir de 0. La forma de ac-
ceder a los elementos de la matriz es utilizando su nombre, seguido de las expresiones enteras
correspondientes a los dos subı́ndices, entre corchetes.
3.2. ARREGLOS 9
Ejemplo 4
double *x;
x=new double;
cin>>*x;
cout<<*x;
delete x;
Además, estos operadores pueden ser usados para la creación de arreglos. Se puede usar la
siguiente sintaxis:
tipo_dato *nombre;
x=new tipo_dato[n];
y se lo puede eliminar mediante el operador delete
delete[] nombre;
El siguiente ejemplo reserva memoria dinámicamente para un vector y una matriz de números
reales:
Ejemplo 5 #include <iostream>
int main()
{
//VECTORES
int n,;
double *vect;
// se reserva memoria para el vector
vect = new double[n];
//MATRICES
3.3.1 Vectores
Un vector contiene elementos almacenados como un arreglo. Se debe incluir la libreria:
3.4 Clases
La Programación Orientada a Objetos permite realizar grandes programas mediante la unión de
elementos más simples que pueden ser diseñados y comprobados de manera independiente del
programa que van a usarlos. A estas piezas, módulos o componentes, que interactúan entre sı́
cuando se ejecuta un programa se les denomina objetos. Estos objetos contienen tanto datos
como las funciones que actúan sobre esos datos. Algunos de estos elementos representan entidades
del mundo real (matrices, personas, cuentas de banco, elementos mecánicos o eléctricos, ...) y
otros pueden ser componentes del ordenador.
La construcción de éstos objetos se realiza mediante la definición de clases. Una clase contiene
una completa y detallada descripción de la información y las funciones que contendrá cada objeto
de esa clase. En C++ las clases son verdaderos tipos de datos definidos por el usuario y pueden
ser utilizados de igual manera que los tipos de datos propios del C++, tales como int o float.
12 CHAPTER 3. CONCEPTOS BÁSICOS DE C++
• públicos: son aquellos a los que se puede acceder libremente desde fuera de la clase.
• privados: solamente pueden ser accedidos por los métodos de la propia clase.
class Nombre_clase
{
private:
Declaración de variables;
public:
Declaración de variables;
Constructores;
Operadores;
Funciones;
};
Ejemplo 6 Crear una clase para almacenar los datos personales de un estudiante:
class alumno
{
private:
string Nombre;
string Apellido;
int edad;
int semestre;
double nota;
public:
//CONSTRUCTRES
alumno()
{
Nombre="S/N";
Apellido="S/N";
edad=-1;
semestre=0;
nota=0.0;
}
//OPERADORES
friend ostream& operator<<(ostream& os,alumno& al);
3.4. CLASES 13
//FUNCIONES
string Escribir_nombre()
{return Nombre;}
int Escribir_edad();
void Modificar_nota(double n)
{Nota=n;}
};
int alumno::Escribir_edad()
{
return edad;
}
Constructores y destructores
Los constructores y destructores deben ser declaradas en la sección pública de la clase, por lo
que se podrán utilizar desde fuera de la misma sin ningún tipo de restricción. Los constructores
deben llevar el mismo nombre de la clase y no tienen valor de retorno, ni siquiera void.
Nombre_Clase(Lista de argumentos);
Los constructores se llaman de modo automático cada vez que se crea un objeto (en el caso
anterior, cada vez que se cree un objeto alumno) y su misión es que todas las variables miembro
estén siempre correctamente inicializadas.
Los destructores son funciones miembro especiales que sirven para eliminar un objeto. El de-
structor realizará procesos necesarios cuando un objeto termine su ámbito temporal, por ejemplo
liberando la memoria dinámica utilizada por dicho objeto o liberando recursos usados. La sintaxis
es la siguiente:
~ Nombre_Clase();
Cuando se define un destructor para una clase, éste es llamado automáticamente cuando se aban-
dona el ámbito en el que fue definido. Esto es ası́ salvo cuando el objeto fue creado dinámicamente
con el operador new, ya que en ese caso es necesario eliminarlo explı́citamente usando el operador
delete.
Funciones miembro
Estas funciones o métodos públicos constituyen la interfaz de la clase que garantiza el buen uso
de los objetos, manteniendo la coherencia de la información. Esto serı́a imposible si se accediera
libre e independientemente a cada variable miembro. Al usuario le es suficiente con saber cómo
comunicarse con un objeto, pero no tiene por qué conocer el funcionamiento interno del mismo.
Las funciones miembro permiten acceder a las variables privadas. Las funciones miembro
deben ser declaradas al interior de la clase y permiten el acceso a las variables privadas o pub-
licas directamente, sin necesidad de re-declararlas o pasarlas como argumento. A las funciones
miembro se accede mediante el operador punto (.) o flecha (− >) dependiendo del caso.
14 CHAPTER 3. CONCEPTOS BÁSICOS DE C++
Nombre_Clase.Nombre_Función();
Funciones amigas
Una función amiga de una clase es una función que no pertenece a la clase, pero que tiene permiso
para acceder a sus variables y funciones miembro privadas por medio de los operadores punto (.)
o flecha (− >), sin tener que recurrir a las funciones miembro públicas de la clase. Si una clase
se declara como amiga(friend) de otra, todas sus funciones miembro son amigas de esta segunda
clase. El carácter de amiga puede restringirse a funciones concretas, que pueden ser miembro de
alguna clase o pueden ser funciones generales que no pertenecen a ninguna clase.
Para declarar una función o una clase como amiga de otra clase, es necesario anteponer friend
a la declaración de la clase o función y dentro de la clase que debe autorizar el acceso a sus datos
privados. Esto se puede hacer de forma indiferente en la zona de los datos o en la de los datos
privados. Un ejemplo de declaración de una clase friend podrı́a ser el que sigue:
class Nombre
{
friend class Amiga;
friend tipo_retorno Funcion(argumentos);
};
Operadores
Los operadores de C++, al igual que las funciones, pueden ser sobrecargados. La sobrecarga de
operadores quiere decir que se pueden redefinir algunos de los operadores existentes en C++ para
que actúen de una manera determinada definida por el programador. Esto puede ser muy útil,
por ejemplo, para definir operaciones matemáticas con objetos tales como vectores, matrices,
grafos, etc. Ası́, sobrecargando adecuadamente los operadores suma (+) y asignación (=), se
puede llegar a sumar dos matrices con una sentencia tan simple como:
A = B + C;
class complejo {
private:
// Datos miembro de la clase "pareja"
int a, b;
3.5. HERENCIA 15
public:
// Constructor
complejo(int a2, int b2);
// Funciones miembro
void Guarda(int a2, int b2);
friend complejo operator= (int a2, int b2);
};
3.5 Herencia
La herencia permite definir una clase modificando una o más clases ya existentes. Estas modifica-
ciones consisten habitualmente en añadir nuevas variables o funciones a una clase que previamente
construida. También se puede redefinir variables o funciones miembro ya existentes.
Las clases de las que se parte en el proceso de herencia se llaman clases base y la nueva
clases que se obtiene se llama clase derivada. Una clase derivada puede a su vez ser una clase
base de un nuevo proceso de derivación, iniciando de esta forma una jerarquización de las clases.
Un objeto de la clase derivada contiene todos los miembros de la clase base y todos los miembros
adicionales de la clase derivada.
La sintaxis general que permite declarar clases derivadas es la siguiente:
};
Si no se especifica ninguno de los dos, por defecto se asume que es private. En el caso
de estructuras jerárquicas se debe permitir que las clases derivadas tengan acceso a las vari-
ables,funciones miembro de las clases base. Para ello, el acceso protected es definido adicional
a private y public. Los miembros definidos en protected permiten que los datos sean inaccesibles
desde el exterior de las clases, pero a la vez, sean accesibles desde las clases derivadas.
Al igual que en la sección anterior, al momento de declarar una clase derivada se debe definir
los respectivos constructores. El constructor de la clase base puede ser omitido en el caso de que
se tenga un constructor por defecto:
Clase_Derivada();
es decir, es llamado automáticamente. Por el contrario, en el caso en el que el constructor de la
clase base disponga de argumentos, al declarar el objeto de la clase derivada se ejecuta primero
el constructor de la clase base
Clase_Derivada(definicion argumentos):Clase_Base(llamado );
OBS 1 Clases y punteros: En C++ es posible trabajar con puntero que apuntan a clases.
Para hacer menos incómodo el trabajo con punteros a estructuras, C++ dispone del operador
flecha (− >) que se utiliza de esta forma:
class base
{
protected:
int a;
double b;
public:
base(int x,double y)
{
a=x;
b=y;
}
double Obt_b()
{
3.5. HERENCIA 17
return b;
}
};
derivada_2(int m):derivada_1(m)
{
n=m;
e=new int [n];
e[0]=a;
for(int i=1;i<n;i++)
{
e[i]=e[i-1]+b+c;
}
}
~ derivada_2()
{
cout<<"llamada al destructor"<<endl;
delete[] e;
}
<<"c=" <<x.c<<endl
<<x.d<<endl;
for(int i=0;i<x.n;i++)
{
cout<<x.e[i]<<" ; ";
}
os<<endl;
return os;
}
};
int main()
{
base x(5,7.5);
derivada_1 y(5);
cout<<"x.b="<<x.Obt_b()<<endl
<<"y.b="<<y.Obt_b()<<endl
<<"a="<<y.Obt_der_a()<<endl;
derivada_2 z(20);
cout<<z;
return 0;
}
3.6 Ejercicios
Ejercicio 3.1: Se dispone de un arreglo de n elementos enteros A que contiene valores
repetidos, esto es, cada elemento puede aparecer más de una vez en el arreglo. Escribir un
programa para remover todos los duplicados del arreglo.
[f (xn )]2
xn+1 = xn −
f (xn + f (xn )) − f (xn )
T :Rn → Rm
x 7→ y = T (x) := Ax + b
donde A ∈ Rm×n y b ∈ Rm .
(a) Implementar una función que reciba A, x, b y calcule la transformación afı́n, de acuerdo
a la siguiente declaración:
double* afin(double** A, double* b, double * x);
(b) Probar la función anterior en un programa que lea un valor α ∈ R desde el teclado y
generar la matriz A y los vectores x, b.
cos α 0 − sin α 1 α
A := 0 1 0 x := 1 b := 2α
sin α 0 cos α α 3α
Ejercicio 3.5: Escribir una función que busque y elimine todas las filas duplicas en una
matriz, es decir, dada una matriz A ∈ Zm×n donde posiblemente para algún par de filas
i, j con i 6= j se tiene que ai1 = aj1 , . . . , ain = ajn , entonces se debe retornar una matriz
A′ ∈ Zp×n tal que p ≤ n donde se mantenga solo una de las filas repetidas. Usar la
declaración:
Escribir una segunda función que reciba m, n, k y retorne una matriz A ∈ Z m×n , donde sus
elementos sean enteros aleatorios en el intervalo [0, k]. Aplicar la función EliminarDuplicados
a la matriz anterior.
Ejercicio 3.6: Dados n + 1 números naturales c1 , . . . , cn y K. La tarea consiste en
determinar si existe un subconjunto S ⊆ {1, . . . , n} tal que:
X
cj = K
j∈S
ser almacenado como un vector c ∈ Nn y la solución puede ser expresada como un vector
binario s ∈ {0, 1}n (si = 1 si el número natural ci es usado en la solución, y si = 0 caso
contrario). Entonces se debe cumplir que:
n
X
ci si = K
i=1
Podemos explorar el espacio de soluciones generando una familia F de subconjuntos {1, . . . , n}
y verificando si alguno de estos subconjuntos es una solución del problema.
Realizar las siguientes tareas:
(a) Definir la clase:
• class subconjunto
{
private:
vector<int> sol;
vector<int> c;
int K;
public:
subconjunto();
subconjunto(int n, int K0);
bool validar_sol(vector<vector<int> > As);
//sobrecargar <<,>>.
};
• subconjunto(): Constructor.
• subconjunto(int n, int K0): Constructor. Generar n números aleatorios
menores que K0 y almacenarlos en c.
• validar sol(vector < vector < int > > As): Cada fila de la matriz As rep-
resenta un vector solución s. Si alguna fila satisface la condición del problema,
entonces dicha fila debe ser almacenada en el vector sol de la clase y se retorna
true.
(b) Recibir una instancia del problema desde teclado o un archivo usando >>.
(c) Generar una matriz aleatoria As ∈ {0, 1}m×n y eliminar filas duplicadas.
(d) Usando la matriz anterior, verificar si en As existe una fila solución del problema.
(e) Presentar la instancia y la solución del problema( si existe) usando <<.
Ejercicio 3.7: La conjetura de Collatz fue enunciada por el matemático Lothar Collatz
en 1937 y a la fecha no se ha resuelto. Se la formula de la siguiente forma: Dada la función
f : N → N: (
n
, si n es par
f (n) = 2
3n + 1, si n es impar
La órbita de un número entero cualquiera se define como las imágenes sucesivas al it-
erar la función. Por ejemplo, si n=13: f (13) = 13 × 3 + 1 = 40, f (f (13)) = 40/2 =
20, f (f (f (13))) = 20/2 = 10, . . . Si observamos este ejemplo, la órbita de 13 es periódica,
es decir, se repite indefinidamente a partir de un momento dado: 13, 40, 20, 10, 5, 16, 8, 4, 2, 1.
La conjetura dice que siempre alcanzaremos el 1 (y por tanto el ciclo 4, 2, 1)
para cualquier número con el que comencemos. Realizar una verificación computa-
cional de la misma.
3.6. EJERCICIOS 21
• Escribir una función que reciba un entero positivo n y retorne una lista con su órbita
hasta alcanzar un 1, o cuando la órbita sea periódica.
• Escribir una segunda función que reciba un entero positivo positivo k y verifique que
se satisface la conjetura para los enteros 2, 3, . . . , k. La función debe retornar una
estructura vector < vector < int > > donde la casilla i del vector corresponde a la
órbita del número i + 2.
• Imprimir la estructura anterior en pantalla.
• La clase objeto debe ser representada como un par de números reales donde la primera
componente representa el peso y la segunda el beneficio.
• La clase mochila usará la siguiente declaración:
void particionamiento(vector < int > &gr1, vector < int > &gr2);