Вы находитесь на странице: 1из 129

SOBRECARGA DE FUNCIONES

Sobrecarga de funciones en C ++
La sobrecarga de funciones es una caracterstica de C ++ en la que dos o ms funciones
pueden tener el mismo nombre pero diferentes parmetros.
La sobrecarga de funciones puede considerarse como un ejemplo de caracterstica de
polimorfismo en C ++.
A continuacin se muestra un simple ejemplo de C ++ para demostrar la sobrecarga de
funciones.
#include <iostream>
using namespace std;

void print(int i) {
cout << " Here is int " << i << endl;
}
void print(double f) {
cout << " Here is float " << f << endl;
}
void print(char* c) {
cout << " Here is char* " << c << endl;
}

int main() {
print(10);
print(10.10);
print("ten");
return 0;
}

Salida:
Aqu es int 10
Aqu est el flotante 10.1
Aqu est char * diez

Funciones que no se pueden sobrecargar en C ++


En C ++, las siguientes declaraciones de funciones no se pueden sobrecargar.
1) Declaraciones de funciones que difieren slo en el tipo de retorno. Por ejemplo, el programa
siguiente falla en la compilacin.
#include<iostream>
int foo() {
return 10;
}

char foo() {
return 'a';
}

int main()
{
char x = foo();
getchar();
return 0;
}
2) Las declaraciones de funcin miembro con el mismo nombre y el nombre parameter-type-list
no se pueden sobrecargar si alguno de ellos es una declaracin de funcin de miembro
esttico. Por ejemplo, el siguiente programa falla en la compilacin.
#include<iostream>
class Test {
static void fun(int i) {}
void fun(int i) {}
};

int main()
{
Test t;
getchar();
return 0;
}

3) Las declaraciones de parmetros que difieren slo en un puntero * frente a una matriz [] son
equivalentes. Es decir, la declaracin de matriz se ajusta para convertirse en una declaracin
de puntero. Slo las dimensiones de la matriz segunda y posterior son significativas en los tipos
de parmetros. Por ejemplo, las siguientes dos declaraciones de funcin son equivalentes.
int fun(int *ptr);
int fun(int ptr[]); // redeclaration of fun(int *ptr)

4) Las declaraciones de parmetros que difieren slo en que uno es un tipo de funcin y el otro
es un puntero al mismo tipo de funcin son equivalentes.
void h(int ());
void h(int (*)()); // redeclaration of h(int())

5) Las declaraciones de parmetros que difieren slo en presencia o ausencia de const y / o


voltiles son equivalentes. Es decir, los especificadores de tipo const y volatile para cada tipo
de parmetro se ignoran al determinar qu funcin est siendo declarada, definida o
llamada. Por ejemplo, el siguiente programa falla en la compilacin con el error "redefinition of`
int f (int) '"
Ejemplo:
#include<iostream>
#include<stdio.h>

using namespace std;

int f ( int x) {
return x+10;
}

int f ( const int x) {


return x+10;
}

int main() {
getchar();
return 0;
}

Slo los especificadores de tipo const y voltiles en el nivel ms externo de la especificacin de


tipo de parmetro se ignoran de esta manera; Constantes y voltiles enterrados dentro de una
especificacin de tipo de parmetro son significativos y se pueden utilizar para distinguir las
declaraciones de funciones sobrecargadas.En particular, para cualquier tipo T,
"Puntero a T", "puntero a T constante" y "puntero a T voltil" se consideran tipos de parmetro
distintos, como son "referencia a T", "referencia a const T" y "referencia a T voltil". Ejemplo,
ver el ejemplo en este comentario publicado por Venki.
6) Dos declaraciones de parmetros que slo difieren en sus argumentos por defecto son
equivalentes. Por ejemplo, el siguiente programa falla en la compilacin con el
error "redefinition of int int (int, int)"
#include<iostream>
#include<stdio.h>

using namespace std;

int f ( int x, int y) {


return x+10;
}

int f ( int x, int y = 10) {


return x+y;
}

int main() {
getchar();
return 0;
}

Sobrecarga de funcin y palabra clave const


Predecir la salida del siguiente programa de C ++.
#include<iostream>
using namespace std;

class Test
{
protected:
int x;
public:
Test (int i):x(i) { }
void fun() const
{
cout << "fun() const called " << endl;
}
void fun()
{
cout << "fun() called " << endl;
}
};

int main()
{
Test t1 (10);
const Test t2 (20);
t1.fun();
t2.fun();
return 0;
}

Salida: El programa anterior compila y ejecuta muy bien, y produce la siguiente salida.
Divertido () llamado
Fun () const llamada

Los dos mtodos 'void fun () const' y 'void fun ()' tienen la misma firma excepto que uno es
const y otro no.Adems, si echamos un vistazo ms de cerca a la salida, observamos que
'const void fun ()' es llamado en el objeto const y 'void fun ()' se llama al objeto non-const.
C ++ permite que los mtodos de miembro se sobrecargen sobre la base del tipo const. La
sobrecarga basada en el tipo const puede ser til cuando una funcin devuelve la referencia o
el puntero. Podemos hacer una constante de funcin, que devuelve una referencia constante o
un puntero const, otra funcin no const, que devuelve una referencia no const o un
puntero. Vea esto para ms detalles.
Qu pasa con los parmetros?
Las reglas relacionadas con los parmetros const son interesantes. Veamos primero dos
ejemplos. El programa 1 falla en la compilacin, pero el programa 2 compila y funciona bien.
// PROGRAM 1 (Fails in compilation)
#include<iostream>
using namespace std;

void fun(const int i)


{
cout << "fun(const int) called ";
}
void fun(int i)
{
cout << "fun(int ) called " ;
}
int main()
{
const int i = 10;
fun(i);
return 0;
}

Salida:
Error de compilador: redefinicin de 'void fun (int)'

// PROGRAM 2 (Compiles and runs fine)


#include<iostream>
using namespace std;

void fun(char *a)


{
cout << "non-const fun() " << a;
}

void fun(const char *a)


{
cout << "const fun() " << a;
}

int main()
{
const char *ptr = "GeeksforGeeks";
fun(ptr);
return 0;
}

Salida:
Const fun () GeeksforGeeks

C ++ permite que las funciones se sobrecargen sobre la base de const-ness de parmetros


slo si el parmetro const es una referencia o un puntero. Es por eso que el programa 1 fall
en la compilacin, pero el programa 2 funcion bien. Esta regla realmente tiene sentido. En el
programa 1, el parmetro 'i' pasa por valor, as que 'i' en fun () es una copia de 'i' en main
(). Por lo tanto, fun () no puede modificar 'i' de main (). Por lo tanto, no importa si 'i' se recibe
como un parmetro const o un parmetro normal. Cuando pasamos por referencia o puntero,
podemos modificar el valor referido o apuntado, por lo que podemos tener dos versiones de
una funcin, una que puede modificar el valor referido o sealado, otro que no puede.
Como un ejercicio, predecir la salida del siguiente programa.
#include<iostream>
using namespace std;

void fun(const int &i)


{
cout << "fun(const int &) called ";
}
void fun(int &i)
{
cout << "fun(int &) called " ;
}
int main()
{
const int i = 10;
fun(i);
return 0;
}

Sobrecarga de funcin y tipo de retorno


En C ++ y Java, las funciones no se pueden sobrecargar si difieren slo en el tipo de retorno.
Por ejemplo, los siguientes programas C ++ y programas Java fallan en la compilacin.
Programa C ++
#include<iostream>
int foo() {
return 10;
}

char foo() { // compiler error; new declaration of foo()


return 'a';
}

int main()
{
char x = foo();
getchar();
return 0;
}

Programa Java
// filename Main.java
public class Main {
public int foo() {
return 10;
}
public char foo() { // compiler error: foo() is already defined
return 'a';
}
public static void main(String args[])
{
}
}
Funciona la sobrecarga con la herencia?
Si tenemos una funcin en la clase base y una funcin con el mismo nombre en la clase
derivada, se puede llamar a la funcin de clase base desde el objeto de clase derivada? Esta
es una pregunta interesante y como un experimento predecir la salida del siguiente
programa C ++ .
#include <iostream>
using namespace std;
class Base
{
public:
int f(int i)
{
cout << "f(int): ";
return i+3;
}
};
class Derived : public Base
{
public:
double f(double d)
{
cout << "f(double): ";
return d+3.3;
}
};
int main()
{
Derived* dp = new Derived;
cout << dp->f(3) << '\n';
cout << dp->f(3.3) << '\n';
delete dp;
return 0;
}

La salida de este programa es:


F (doble): 6,3
F (doble): 6,6

En lugar de la supuesta salida:


F (int): 6
F (doble): 6,6

Sobrecarga no funciona para la clase derivada en lenguaje de programacin C ++. No hay una
resolucin de sobrecarga entre Base y Derivada. El compilador examina el mbito de Derived,
encuentra la nica funcin "doble f (doble)" y lo llama. Nunca perturba con el alcance de la
Base. En C ++, no hay sobrecarga en mbitos - los mbitos de clase derivados no son una
excepcin a esta regla general. (Vea esto para ms ejemplos)
Referencia: FAQs tcnicos en www.stroustrup.com
Ahora considere la versin Java de este programa:
class Base
{
public int f(int i)
{
System.out.print("f (int): ");
return i+3;
}
}
class Derived extends Base
{
public double f(double i)
{
System.out.print("f (double) : ");
return i + 3.3;
}
}
class myprogram3
{
public static void main(String args[])
{
Derived obj = new Derived();
System.out.println(obj.f(3));
System.out.println(obj.f(3.3));
}
}

La salida del programa anterior es:


F (int): 6
F (doble): 6,6

As que en la sobrecarga de Java funciona a travs de mbitos contrarios a C ++. El


compilador Java determina la versin correcta del mtodo sobrecargado a ejecutar en tiempo
de compilacin basado en el tipo de argumento utilizado para llamar al mtodo y los
parmetros de los mtodos sobrecargados de ambas clases reciben los valores de los
argumentos utilizados en la llamada y ejecutan el mtodo sobrecargado.
Por ltimo, vamos a probar la salida del siguiente programa C # :
using System;
class Base
{
public int f(int i)
{
Console.Write("f (int): ");
return i + 3;
}
}
class Derived : Base
{
public double f(double i)
{
Console.Write("f (double) : ");
return i+3.3;
}
}
class MyProgram
{
static void Main(string[] args)
{
Derived obj = new Derived();
Console.WriteLine(obj.f(3));
Console.WriteLine(obj.f(3.3));
Console.ReadKey(); // write this line if you use visual studio
}
}

Nota: Console.ReadKey () se usa para detener la consola. Es similar a getch como en C / C


++.
La salida del programa anterior es:
F (doble): 6,3
F (doble): 6,6
En lugar de la salida supuesta
F (int): 6
F (doble): 6,6

La razn es la misma explicada en el programa C ++. Como C ++, no hay resolucin de


sobrecarga entre la clase Base y la clase Derived. En C #, no hay sobrecarga en los mbitos
derivados de mbitos de clase no son una excepcin a esta regla general. Esto es lo mismo
que C + + porque C # est diseado para estar mucho ms cerca de C + +, de acuerdo con
el hejlsberg Anders el creador de lenguaje C #. Este artculo es aportado por Pravasi Meet.

Puede () estar sobrecargado en C ++?


Predecir la salida del siguiente programa de C ++.
#include <iostream>
using namespace std;
int main(int a)
{
cout << a << "\n";
return 0;
}
int main(char *a)
{
cout << a << endl;
return 0;
}
int main(int a, int b)
{
cout << a << " " << b;
return 0;
}
int main()
{
main(3);
main("C++");
main(9, 6);
return 0;
}

El programa anterior falla en la compilacin y produce advertencias y errores (vea esto para
advertencias y errores producidos). Puede obtener diferentes errores en diferentes
compiladores.
Para sobrecargar la funcin main () en C ++, es necesario utilizar class y declarar la funcin
principal como miembro. Tenga en cuenta que main no es palabra reservada en lenguajes de
programacin como C, C ++, Java y C #. Por ejemplo, podemos declarar una variable cuyo
nombre es principal, intente debajo del ejemplo:
#include <iostream>
int main()
{
int main = 10;
std::cout << main;
return 0;
}

Ouput:
10

El siguiente programa muestra la sobrecarga de la funcin main () en una clase.


#include <iostream>
using namespace std;
class Test
{
public:
int main(int s)
{
cout << s << "\n";
return 0;
}
int main(char *s)
{
cout << s << endl;
return 0;
}
int main(int s ,int m)
{
cout << s << " " << m;
return 0;
}
};
int main()
{
Test obj;
obj.main(3);
obj.main("I love C++");
obj.main(9, 6);
return 0;
}

El resultado del programa es:


3
Me encanta C ++
9 6

Argumento por defecto

Argumentos predeterminados en C ++
Un argumento predeterminado es un valor proporcionado en la declaracin de funcin que el
compilador asigna automticamente si el llamador de la funcin no proporciona un valor para el
argumento con valor predeterminado.
A continuacin, se muestra un ejemplo simple de C ++ para demostrar el uso de argumentos
predeterminados. No tenemos que escribir 3 funciones de suma, slo una funcin funciona
mediante el uso de valores por defecto para 3 y 4 argumentos.
#include<iostream>
using namespace std;

// A function with default arguments, it can be called with


// 2 arguments or 3 arguments or 4 arguments.
int sum(int x, int y, int z=0, int w=0)
{
return (x + y + z + w);
}

/* Drier program to test above function*/


int main()
{
cout << sum(10, 15) << endl;
cout << sum(10, 15, 25) << endl;
cout << sum(10, 15, 25, 30) << endl;
return 0;
}

Salida:
25
50
80
Una vez que el valor predeterminado se utiliza para un argumento, todos los argumentos
subsiguientes deben tener valor predeterminado.
// Invalid because z has default value, but w after it
// doesn't have default value
int sum(int x, int y, int z=0, int w)

Funciones en lnea

Funciones en lnea en C ++
La funcin en lnea es una de las caractersticas importantes de C ++. Por lo tanto, vamos a
entender por qu las funciones en lnea se utilizan y cul es el propsito de la funcin en lnea?
Cuando el programa ejecuta la instruccin de llamada de funcin, la CPU almacena la direccin
de memoria de la instruccin despus de la llamada de funcin, copia los argumentos de la
funcin en la pila y finalmente transfiere el control a la funcin especificada. A continuacin, la
CPU ejecuta el cdigo de funcin, almacena el valor de retorno de la funcin en una posicin /
registro de memoria predefinida y devuelve el control a la funcin de llamada. Esto puede
convertirse en sobrecarga si el tiempo de ejecucin de la funcin es menor que el tiempo de
conmutacin de la funcin llamante a la llamada funcin (callee). Para funciones que son
grandes y / o realizan tareas complejas, la sobrecarga de la llamada de funcin es
generalmente insignificante en comparacin con la cantidad de tiempo que tarda la funcin en
ejecutarse. Sin embargo, para las funciones pequeas y comnmente utilizadas, el tiempo
necesario para realizar la llamada de funcin suele ser mucho ms que el tiempo necesario
para ejecutar realmente el cdigo de la funcin. Esta sobrecarga se produce para funciones
pequeas porque el tiempo de ejecucin de la pequea funcin es menor que el tiempo de
conmutacin.
C ++ proporciona funciones en lnea para reducir la sobrecarga de la llamada de
funcin. Funcin en lnea es una funcin que se ampla en lnea cuando se llama. Cuando se
llama a la funcin en lnea cdigo entero de la funcin en lnea se inserta o sustituye en el
punto de llamada de funcin en lnea. Esta sustitucin se realiza por el compilador de C ++ en
tiempo de compilacin. La funcin en lnea puede aumentar la eficiencia si es pequea.
La sintaxis para definir la funcin inline es:
Inline return-type nombre-funcin (parmetros)
{
// cdigo de funcin
}
Recuerde, inlining es slo una peticin al compilador, no un comando. El compilador puede
ignorar la solicitud de inlining. El compilador no puede realizar inlining en tales circunstancias
como:
1) Si una funcin contiene un bucle. (Para, while, do-while)
2) Si una funcin contiene variables estticas.
3) Si una funcin es recursiva.
4) Si un tipo de retorno de funcin es distinto de vaco, y la declaracin de devolucin no existe
en el cuerpo de la funcin.
5) Si una funcin contiene una sentencia switch o goto.
Las funciones en lnea ofrecen las siguientes ventajas:
1) No se produce la sobrecarga de llamada de funcin.
2) Tambin guarda la sobrecarga de las variables push / pop en la pila cuando se llama a la
funcin.
3) Tambin ahorra la sobrecarga de una llamada de vuelta de una funcin.
4) Cuando inline una funcin, puede permitir al compilador para realizar la optimizacin de
contexto especfico en el cuerpo de la funcin. Tales optimizaciones no son posibles para
llamadas de funcin normales. Otras optimizaciones se pueden obtener considerando los flujos
de contexto de llamada y el contexto llamado.
5) La funcin en lnea puede ser til (si es pequea) para sistemas embebidos porque inline
puede producir menos cdigo que el prembulo y retorno de llamada de funcin.
Desventajas de la funcin en lnea:
1) Las variables agregadas de la funcin inline consumen registros adicionales, despus de la
funcin de alineacin si el nmero de variables que van a utilizar el registro aumenta que
pueden crear sobrecarga en la utilizacin de recursos de variable de registro. Esto significa que
cuando el cuerpo de la funcin en lnea es sustituido en el punto de la llamada de funcin, el
nmero total de variables utilizadas por la funcin tambin se inserta. Por lo tanto, el nmero de
registro que va a ser utilizado para las variables tambin se incrementar. As que si despus
de la funcin inlining los nmeros de las variables aumentan drsticamente entonces
seguramente causara una sobrecarga en la utilizacin del registro.
2) Si utiliza demasiadas funciones en lnea, el tamao del archivo ejecutable binario ser
grande debido a la duplicacin del mismo cdigo.
3) Demasiado inlining tambin puede reducir la tasa de xito de cach de instrucciones,
reduciendo as la velocidad de la bsqueda de instrucciones de la memoria cach a la de la
memoria primaria.
4) La funcin en lnea puede aumentar la sobrecarga del tiempo de compilacin si alguien
cambia el cdigo dentro de la funcin en lnea, entonces toda la ubicacin de la llamada tiene
que ser recompilada porque el compilador necesitara reemplazar todo el cdigo una vez ms
para reflejar los cambios; de lo contrario continuar con la funcionalidad antigua .
5) Las funciones en lnea pueden no ser tiles para muchos sistemas embebidos. Porque en
sistemas embebidos el tamao del cdigo es ms importante que la velocidad.
6) Las funciones en lnea pueden causar golpear porque inlining puede aumentar el tamao del
archivo ejecutable binario. Tratar en la memoria hace que el rendimiento de la computadora se
degrade.
El siguiente programa demuestra el uso del uso de la funcin en lnea.
#include <iostream>
using namespace std;
inline int cube(int s)
{
return s*s*s;
}
int main()
{
cout << "The cube of 3 is: " << cube(3) << "\n";
return 0;
} //Output: The cube of 3 is: 27

Funcin y clases en lnea:


Tambin es posible definir la funcin inline dentro de la clase. De hecho, todas las funciones
definidas dentro de la clase estn implcitamente en lnea. Por lo tanto, todas las restricciones
de las funciones en lnea tambin se aplican aqu. Si necesita declarar explcitamente la
funcin en lnea en la clase, simplemente declare la funcin dentro de la clase y defnala fuera
de la clase usando palabras clave en lnea.
Por ejemplo:
class S
{
public:
inline int square(int s) // redundant use of inline
{
// this function is automatically inline
// function body
}
};

El estilo anterior es considerado como un mal estilo de programacin. El mejor estilo de


programacin es simplemente escribir el prototipo de la funcin dentro de la clase y
especificarla como una lnea en la definicin de la funcin.
Por ejemplo:
class S
{
public:
int square(int s); // declare the function
};

inline int S::square(int s) // use inline prefix


{

El siguiente programa demuestra este concepto:


#include <iostream>
using namespace std;
class operation
{
int a,b,add,sub,mul;
float div;
public:
void get();
void sum();
void difference();
void product();
void division();
};
inline void operation :: get()
{
cout << "Enter first value:";
cin >> a;
cout << "Enter second value:";
cin >> b;
}

inline void operation :: sum()


{
add = a+b;
cout << "Addition of two numbers: " << a+b << "\n";
}

inline void operation :: difference()


{
sub = a-b;
cout << "Difference of two numbers: " << a-b << "\n";
}

inline void operation :: product()


{
mul = a*b;
cout << "Product of two numbers: " << a*b << "\n";
}

inline void operation ::division()


{
div=a/b;
cout<<"Division of two numbers: "<<a/b<<"\n" ;
}

int main()
{
cout << "Program using inline function\n";
operation s;
s.get();
s.sum();
s.difference();
s.product();
s.division();
return 0;
}

Salida:
Introduzca el primer valor: 45
Introduzca el segundo valor: 15
Adicin de dos nmeros: 60
Diferencia de dos nmeros: 30
Producto de dos nmeros: 675
Divisin de dos nmeros: 3
Qu est mal con macro?
Los lectores familiarizados con el lenguaje C saben que el lenguaje C utiliza macro. El
preprocesador reemplaza todas las macro llamadas directamente dentro del cdigo de la
macro. Se recomienda utilizar siempre la funcin en lnea en lugar de la macro. Segn el Dr.
Bjarne Stroustrup el creador de C ++ que las macros casi nunca son necesarias en C ++ y son
error propenso. Hay algunos problemas con el uso de macros en C ++. Macro no puede tener
acceso a miembros privados de clase. Las macros parecen llamadas de funcin, pero en
realidad no lo son.
Ejemplo:
#include <iostream>
using namespace std;
class S
{
int m;
public:
#define MAC(S::m) // error
};

El compilador C ++ comprueba los tipos de argumento de las funciones en lnea y las


conversiones necesarias se realizan correctamente. La macro de preprocesador no es capaz
de hacer esto. Otra cosa es que las macros son gestionadas por el preprocesador y las
funciones en lnea son gestionadas por el compilador C ++.
Recuerde: Es cierto que todas las funciones definidas dentro de la clase estn implcitamente
en lnea y el compilador C ++ realizar una llamada en lnea de estas funciones, pero el
compilador C ++ no puede realizar inlining si la funcin es virtual. La razn es la llamada a una
funcin virtual se resuelve en tiempo de ejecucin en lugar de tiempo de compilacin. Virtual
significa esperar hasta el tiempo de ejecucin y en lnea significa durante la compilacin, si el
compilador no sabe qu funcin se llamar, cmo se puede realizar inlining?
Otra cosa a recordar es que slo es til hacer la funcin inline si el tiempo gastado durante una
llamada de funcin es ms comparado con el tiempo de ejecucin del cuerpo de la funcin. Un
ejemplo donde la funcin inline no tiene ningn efecto en absoluto:
inline void show()
{
cout << "value of S = " << S << endl;
}
La funcin anterior tarda bastante tiempo en ejecutarse. En general, la funcin que realiza la
operacin de entrada de salida (E / S) no debe definirse como lnea porque pasa una cantidad
considerable de tiempo.Tcnicamente inlining de la funcin show () es de valor limitado porque
la cantidad de tiempo que la declaracin de E / S tomar excede con mucho la sobrecarga de
una llamada de funcin.
Dependiendo del compilador que est utilizando el compilador puede mostrar advertencia si la
funcin no se expande en lnea. Los lenguajes de programacin como Java y C # no admiten
funciones en lnea.
Pero en Java, el compilador puede realizar inlining cuando se llama al mtodo final pequeo,
porque los mtodos finales no pueden ser reemplazados por subclases y la llamada a un
mtodo final se resuelve en tiempo de compilacin. En C # JIT compilador tambin puede
optimizar el cdigo mediante la creacin de pequeas llamadas de funcin (como reemplazar el
cuerpo de una pequea funcin cuando se llama en un bucle).
La ltima cosa a tener en cuenta que las funciones en lnea son la caracterstica valiosa de C +
+. Un uso apropiado de la funcin en lnea puede proporcionar mejora de rendimiento, pero si
las funciones en lnea se utilizan arbitrariamente, entonces no pueden proporcionar un mejor
resultado. En otras palabras, no esperes un mejor rendimiento del programa. No realice todas
las funciones en lnea. Es mejor mantener las funciones en lnea tan pequeas como sea
posible.
Referencias:
1) C ++ efectivo, Scott Meyers
2) http://www.parashift.com/c++-faq/inline-and-perf.html
3) http://www.cplusplus.com/forum/articles/20600/
4) Pensando en C ++, Volumen 1, Bruce Eckel .
5) C ++ la referencia completa, Herbert Schildt
Este artculo es aportado por Meet Pravasi .

Nuevo y eliminar

Malloc () vs nuevo
A continuacin se muestran las diferencias entre malloc () y operator new.
1) constructores de nuevas llamadas, mientras que malloc () no. De hecho, los tipos de datos
primitivos (char, int, float .. etc) tambin se pueden inicializar con new. Por ejemplo, debajo de
las impresiones del programa 10.
#include<iostream>

using namespace std;

int main()
{
int *n = new int(10); // initialization with new()
cout<<*n;
getchar();
return 0;
}

2) new es un operador, mientras que malloc () es un fucntion.


3) nuevo devuelve el tipo de datos exacto, mientras que malloc () devuelve void *.

Eliminar y libre () en C + +
En C ++, slo se debe utilizar el operador delete para los punteros que apuntan a la memoria
asignada utilizando el nuevo operador o para un puntero NULL, y free () slo debe utilizarse
para los punteros que apuntan a la memoria asignada usando malloc () o para Un puntero
NULL.
#include<stdio.h>
#include<stdlib.h>
int main()
{
int x;
int *ptr1 = &x;
int *ptr2 = (int *)malloc(sizeof(int));
int *ptr3 = new int;
int *ptr4 = NULL;

/* delete Should NOT be used like below because x is allocated


on stack frame */
delete ptr1;

/* delete Should NOT be used like below because x is allocated


using malloc() */
delete ptr2;

/* Correct uses of delete */


delete ptr3;
delete ptr4;

getchar();
return 0;
}

Clase y Objeto

Conceptos bsicos de programacin orientada a


objetos utilizando C ++
Objeto: Los objetos son entidades de tiempo de ejecucin bsicas en un sistema orientado a
objetos, los objetos son instancias de una clase. Estos son tipos definidos de datos definidos
por el usuario.
ex:
class person
{
char name[20];
int id;
public:
void getdetails(){}
};

int main()
{
person p1; //p1 is a object
}

Objeto ocupan espacio en la memoria y tienen una direccin asociada como un registro en
pascal o estructura o unin en C.
Cuando se ejecuta un programa, los objetos interactan enviando mensajes entre s.
Cada objeto contiene datos y cdigo para manipular los datos. Los objetos pueden interactuar
sin tener que conocer detalles de los datos o cdigo de cada uno, basta con saber el tipo de
mensaje aceptado y el tipo de respuesta devuelta por los objetos.

Clase: Clase es un plano de datos y funciones o mtodos. La clase no toma ningn espacio.
Sintaxis para la clase:
class class_name
{
private:
//data members and member functions declarations
public:
//data members and member functions declarations
protected:
//data members and member functions declarations
};

Clase es un tipo de datos definido por el usuario como estructuras y uniones en C.


Por defecto las variables de clase son privadas pero en caso de estructura es pblica. En
ejemplo arriba la persona es una clase.

Encapsulacin y abstraccin de datos: El encapsulamiento (peinado) de datos y funciones


en una sola unidad se conoce como encapsulacin. Los datos no son accesibles para el
mundo exterior y slo las funciones que estn envolviendo en la clase pueden acceder a
ella. Este aislamiento de los datos de acceso directo por el programa se denomina ocultar
datos o ocultar informacin.
La abstraccin de datos hace referencia a, proporcionando slo la informacin necesaria al
mundo exterior y ocultando detalles de implementacin. Por ejemplo, considere un Complejo
de clase con funciones pblicas como getReal () y getImag (). Podemos implementar la clase
como una matriz de tamao 2 o como dos variables. La ventaja de las abstracciones es,
podemos cambiar la implementacin en cualquier punto, los usuarios de la clase compleja no
se vern afectados ya que la interfaz del mtodo permanece igual. Si nuestra implementacin
fuera pblica, no habramos podido cambiarla.

Herencia: la herencia es el proceso mediante el cual los objetos de una clase adquieren las
propiedades de los objetos de otra clase. Apoya el concepto de clasificacin jerrquica. La
herencia proporciona la reutilizacin.Esto significa que podemos agregar funciones adicionales
a una clase existente sin modificarla.

Polimorfismo: polimorfismo significa capacidad de tomar ms de una forma. Una operacin


puede mostrar comportamientos diferentes en diferentes instancias. El comportamiento
depende de los tipos de datos utilizados en la operacin.
C ++ soporta la sobrecarga del operador y la sobrecarga de funciones.
La sobrecarga del operador es el proceso de hacer que un operador exhiba diferentes
comportamientos en diferentes instancias es conocido como sobrecarga del operador.
La sobrecarga de funciones utiliza un solo nombre de funcin para realizar diferentes tipos de
tareas.
Polimorfismo se utiliza ampliamente en la implementacin de la herencia.

Encuadernacin dinmica: En el enlace dinmico, el cdigo a ejecutar en respuesta a la


llamada a la funcin se decide en tiempo de ejecucin. C ++ tiene funciones virtuales para
apoyar esto.

Paso de mensajes: los objetos se comunican entre s mediante el envo y la recepcin de


informacin entre s.Un mensaje para un objeto es una peticin para la ejecucin de un
procedimiento y por lo tanto invocar una funcin en el objeto receptor que genera los
resultados deseados. El paso del mensaje implica especificar el nombre del objeto, el nombre
de la funcin y la informacin a enviar.
Este artculo es aportado por Vankayala Karunakar

Estructura vs clase en C ++
En C ++, una estructura es igual que la clase excepto las siguientes diferencias:
1) Los miembros de una clase son privados por defecto y los miembros de struct son pblicos
de forma predeterminada.
Por ejemplo, el programa 1 falla en la compilacin y el programa 2 funciona bien.
// Program 1
#include <stdio.h>

class Test {
int x; // x is private
};
int main()
{
Test t;
t.x = 20; // compiler error because x is private
getchar();
return 0;
}

// Program 2
#include <stdio.h>

struct Test {
int x; // x is public
};
int main()
{
Test t;
t.x = 20; // works fine because x is public
getchar();
return 0;
}

2) Al derivar una estructura de una clase / struct, el especificador de acceso predeterminado


para una clase / estructura base es pblico. Y al derivar una clase, el especificador de acceso
predeterminado es privado.
Por ejemplo, el programa 3 falla en la compilacin y el programa 4 funciona bien.
// Program 3
#include <stdio.h>

class Base {
public:
int x;
};

class Derived : Base { }; // is equilalent to class Derived : private Base {}

int main()
{
Derived d;
d.x = 20; // compiler error becuase inheritance is private
getchar();
return 0;
}

// Program 4
#include <stdio.h>

class Base {
public:
int x;
};

struct Derived : Base { }; // is equilalent to struct Derived : public Base {}

int main()
{
Derived d;
d.x = 20; // works fine becuase inheritance is public
getchar();
return 0;
}

Puede una clase C ++ tener un objeto de tipo


self?
Una declaracin de clase puede contener un objeto esttico de tipo self, tambin puede tener
un puntero a self type, pero no puede tener un objeto no esttico de tipo self.
Por ejemplo, el programa siguiente funciona bien.
// A class can have a static member of self type
#include<iostream>

using namespace std;

class Test {
static Test self; // works fine

/* other stuff in class*/

};

int main()
{
Test t;
getchar();
return 0;
}

Y el siguiente programa tambin funciona bien.


// A class can have a pointer to self type
#include<iostream>

using namespace std;

class Test {
Test * self; //works fine

/* other stuff in class*/


};

int main()
{
Test t;
getchar();
return 0;
}

Pero el siguiente programa genera error de compilacin "field` self 'tiene un tipo incompleto "
// A class cannot have non-static object(s) of self type.
#include<iostream>

using namespace std;

class Test {
Test self; // Error

/* other stuff in class*/

};

int main()
{
Test t;
getchar();
return 0;
}

Si un objeto no esttico es miembro entonces la declaracin de clase es incompleta y el


compilador no tiene forma de averiguar el tamao de los objetos de la clase.
Las variables estticas no contribuyen al tamao de los objetos. As que no hay problema en el
clculo de tamao con variables estticas de tipo auto.
Para un compilador, todos los punteros tienen un tamao fijo independientemente del tipo de
datos al que apuntan, por lo que no hay problema con esto tambin.

Por qu el tamao de una clase vaca no es cero


en C ++?
Predice la salida del siguiente programa?
#include<iostream>
using namespace std;

class Empty {};

int main()
{
cout << sizeof(Empty);
return 0;
}

Salida:
1
El tamao de una clase vaca no es cero. Es 1 byte en general. Es distinto de cero para
asegurar que los dos objetos diferentes tendrn direcciones diferentes. Vea el siguiente
ejemplo.
#include<iostream>
using namespace std;

class Empty { };

int main()
{
Empty a, b;

if (&a == &b)
cout << "impossible " << endl;
else
cout << "Fine " << endl;

return 0;
}

Salida:
Multa

Por la misma razn (objetos diferentes deben tener direcciones diferentes), "nuevo" siempre
devuelve punteros a objetos distintos. Vea el siguiente ejemplo.
#include<iostream>
using namespace std;

class Empty { };

int main()
{
Empty* p1 = new Empty;
Empty* p2 = new Empty;

if (p1 == p2)
cout << "impossible " << endl;
else
cout << "Fine " << endl;

return 0;
}

Salida:
Multa

Ahora adivine la salida del siguiente programa (Esto es complicado)


#include<iostream>
using namespace std;

class Empty { };

class Derived: Empty { int a; };

int main()
{
cout << sizeof(Derived);
return 0;
}

Salida (con el compilador de GCC. Vea esto ):


4

Observe que la salida no es mayor que 4. Hay una regla interesante que dice que una clase
base vaca no necesita ser representada por un byte separado. As que los compiladores son
libres de hacer la optimizacin en caso de clases base vacas. Como un ejercicio, pruebe el
siguiente programa en su compilador.
// Thanks to Venki for suggesting this code.
#include <iostream>
using namespace std;

class Empty
{};

class Derived1 : public Empty


{};

class Derived2 : virtual public Empty


{};

class Derived3 : public Empty


{
char c;
};

class Derived4 : virtual public Empty


{
char c;
};

class Dummy
{
char c;
};

int main()
{
cout << "sizeof(Empty) " << sizeof(Empty) << endl;
cout << "sizeof(Derived1) " << sizeof(Derived1) << endl;
cout << "sizeof(Derived2) " << sizeof(Derived2) << endl;
cout << "sizeof(Derived3) " << sizeof(Derived3) << endl;
cout << "sizeof(Derived4) " << sizeof(Derived4) << endl;
cout << "sizeof(Dummy) " << sizeof(Dummy) << endl;

return 0;
}
Miembros estticos

Algunos hechos interesantes acerca de las


funciones de miembro esttico en C ++
1) Las funciones de miembro esttico no tienen este puntero .
Por ejemplo, el siguiente programa falla en la compilacin con error "` this 'no est disponible
para las funciones de miembro esttico "
#include<iostream>
class Test {
static Test * fun() {
return this; // compiler error
}
};

int main()
{
getchar();
return 0;
}

2) Una funcin de miembro esttico no puede ser virtual (Ver este G-Hecho)
3) Las declaraciones de funcin miembro con el mismo nombre y el nombre parameter-type-list
no se pueden sobrecargar si alguna de ellas es una declaracin de funcin de miembro
esttico.
Por ejemplo, el siguiente programa falla en la compilacin con error " 'void Test :: fun ()' y` static
void Test :: fun () 'no se puede sobrecargar "
#include<iostream>
class Test {
static void fun() {}
void fun() {} // compiler error
};

int main()
{
getchar();
return 0;
}

4) Una funcin miembro esttica no se puede declarar const , voltil o const voltil .
Por ejemplo, el siguiente programa falla en la compilacin con error "static
member function` static void Test :: fun () 'no puede tener `const' mtodo qualificador"
#include<iostream>
class Test {
static void fun() const { // compiler error
return;
}
};

int main()
{
getchar();
return 0;
}

Por favor escriba comentarios si encuentra algo incorrecto, o si desea compartir ms


informacin sobre el tema discutido anteriormente.
Referencias:

Miembros de datos estticos en C ++


Predecir la salida del siguiente programa de C ++:
#include <iostream>
using namespace std;

class A
{
public:
A() { cout << "A's Constructor Called " << endl; }
};

class B
{
static A a;
public:
B() { cout << "B's Constructor Called " << endl; }
};

int main()
{
B b;
return 0;
}

Salida:
El constructor de B llamado

El programa anterior slo llama al constructor de B, no llama al constructor de A. La razn de


esto es simple, losmiembros estticos slo se declaran en la declaracin de clase, no se
define. Deben ser definidos explcitamente fuera de la clase usando el operador de resolucin
de alcance .
Si tratamos de acceder al miembro esttico 'a' sin una definicin explcita de l, obtendremos
un error de compilacin. Por ejemplo, el siguiente programa falla en la compilacin.
#include <iostream>
using namespace std;

class A
{
int x;
public:
A() { cout << "A's constructor called " << endl; }
};

class B
{
static A a;
public:
B() { cout << "B's constructor called " << endl; }
static A getA() { return a; }
};

int main()
{
B b;
A a = b.getA();
return 0;
}

Salida:
Error del compilador: referencia no definida a `B :: a '

Si aadimos la definicin de a, el programa funcionar bien y llamar al constructor de


A. Consulte el siguiente programa.
#include <iostream>
using namespace std;

class A
{
int x;
public:
A() { cout << "A's constructor called " << endl; }
};

class B
{
static A a;
public:
B() { cout << "B's constructor called " << endl; }
static A getA() { return a; }
};

A B::a; // definition of a

int main()
{
B b1, b2, b3;
A a = b1.getA();

return 0;
}

Salida:
El constructor de A llamado
El constructor de B llamado
El constructor de B llamado
El constructor de B llamado

Tenga en cuenta que el programa anterior llama al constructor B 3 veces para 3 objetos (b1, b2
y b3), pero llama al constructor de A slo una vez. La razn es que los miembros estticos se
comparten entre todos los objetos.Es por eso que tambin se les conoce como miembros de
clase o campos de clase . Adems, se puede acceder a los miembros estticos sin ningn
objeto , consulte el siguiente programa donde se accede al miembro esttico 'a' sin ningn
objeto.
#include <iostream>
using namespace std;

class A
{
int x;
public:
A() { cout << "A's constructor called " << endl; }
};

class B
{
static A a;
public:
B() { cout << "B's constructor called " << endl; }
static A getA() { return a; }
};

A B::a; // definition of a

int main()
{
// static member 'a' is accessed without any object of B
A a = B::getA();

return 0;
}

Salida:
El constructor de A llamado

'Este' Puntero

'Este' puntero en C + +
El puntero 'this' se pasa como un argumento oculto a todas las llamadas de funcin de miembro
no esttico y est disponible como una variable local dentro del cuerpo de todas las funciones
no estticas. 'Este' puntero es un puntero constante que contiene la direccin de memoria del
objeto actual. El puntero 'this' no est disponible en las funciones miembro estticas, ya que las
funciones miembro estticas pueden ser llamadas sin ningn objeto (con nombre de clase).
Para una clase X, el tipo de este puntero es 'X * const'. Adems, si una funcin miembro de X
se declara como const, entonces el tipo de este puntero es 'const X * const' (ver este GFact )
A continuacin se describen las situaciones en las que se utiliza el puntero 'this':
1) Cuando el nombre de la variable local es igual al nombre del miembro
#include<iostream>
using namespace std;

/* local variable is same as a member's name */


class Test
{
private:
int x;
public:
void setX (int x)
{
// The 'this' pointer is used to retrieve the object's x
// hidden by the local variable 'x'
this->x = x;
}
void print() { cout << "x = " << x << endl; }
};

int main()
{
Test obj;
int x = 20;
obj.setX(x);
obj.print();
return 0;
}

Salida:
X = 20

Para los constructores, la lista de inicializacin tambin se puede usar cuando el nombre del
parmetro es igual al nombre del miembro.

2) Para devolver referencia al objeto llamante


/* Reference to the calling object can be returned */
Test& Test::func ()
{
// Some processing
return *this;
}

Cuando se devuelve una referencia a un objeto local, la referencia devuelta se puede utilizar
para encadenar las llamadas de funcin en un nico objeto.
#include<iostream>
using namespace std;

class Test
{
private:
int x;
int y;
public:
Test(int x = 0, int y = 0) { this->x = x; this->y = y; }
Test &setX(int a) { x = a; return *this; }
Test &setY(int b) { y = b; return *this; }
void print() { cout << "x = " << x << " y = " << y << endl; }
};

int main()
{
Test obj1(5, 5);

// Chained function calls. All calls modify the same object


// as the same object is returned by reference
obj1.setX(10).setY(20);

obj1.print();
return 0;
}

Salida:
X = 10 y = 20

Ejercicio:
Predecir el resultado de los siguientes programas. Si hay errores de compilacin, entonces
solucionarlos.
Pregunta 1
#include<iostream>
using namespace std;
class Test
{
private:
int x;
public:
Test(int x = 0) { this->x = x; }
void change(Test *t) { this = t; }
void print() { cout << "x = " << x << endl; }
};

int main()
{
Test obj(5);
Test *ptr = new Test (10);
obj.change(ptr);
obj.print();
return 0;
}

Pregunta 2
#include<iostream>
using namespace std;

class Test
{
private:
int x;
int y;
public:
Test(int x = 0, int y = 0) { this->x = x; this->y = y; }
static void fun1() { cout << "Inside fun1()"; }
static void fun2() { cout << "Inside fun2()"; this->fun1(); }
};

int main()
{
Test obj;
obj.fun2();
return 0;
}

Pregunta 3
#include<iostream>
using namespace std;

class Test
{
private:
int x;
int y;
public:
Test (int x = 0, int y = 0) { this->x = x; this->y = y; }
Test setX(int a) { x = a; return *this; }
Test setY(int b) { y = b; return *this; }
void print() { cout << "x = " << x << " y = " << y << endl; }
};
int main()
{
Test obj1;
obj1.setX(10).setY(20);
obj1.print();
return 0;
}

Pregunta 4
#include<iostream>
using namespace std;

class Test
{
private:
int x;
int y;
public:
Test(int x = 0, int y = 0) { this->x = x; this->y = y; }
void setX(int a) { x = a; }
void setY(int b) { y = b; }
void destroy() { delete this; }
void print() { cout << "x = " << x << " y = " << y << endl; }
};

int main()
{
Test obj;
obj.destroy();
obj.print();
return 0;
}

Tipo de puntero 'this' en C ++


En C ++, este puntero se pasa como un argumento oculto a todas las llamadas de funcin de
miembro no esttico. El tipo de esto depende de la declaracin de la funcin. Si la funcin
miembro de una clase X esdeclarada const , el tipo de sta es const X * (ver cdigo 1 a
continuacin), Si la funcin miembro es declaradavoltil , el tipo de sta es voltil X * (vase el
cdigo 2 ms adelante), y si la funcin miembro es declarada const voltil , el tipo
de sta es constante voltil X * (ver cdigo 3).
Cdigo 1
#include<iostream>
class X {
void fun() const {
// this is passed as hidden argument to fun().
// Type of this is const X*
}
};

Cdigo 2
#include<iostream>
class X {
void fun() volatile {
// this is passed as hidden argument to fun().
// Type of this is volatile X*
}
};
Cdigo 3
#include<iostream>
class X {
void fun() const volatile {
// this is passed as hidden argument to fun().
// Type of this is const volatile X*
}
};

"Eliminar esto" en C ++
Idealmente, el operador delete no debe utilizarse para este puntero. Sin embargo, si se usan,
deben considerarse los siguientes puntos.
1) el operador de eliminacin slo funciona para los objetos asignados mediante el
operador nuevo (Verhttp://geeksforgeeks.org/?p=8539 ). Si el objeto se crea utilizando
new, entonces podemos eliminarlo, de lo contrario el comportamiento es indefinido.
class A
{
public:
void fun()
{
delete this;
}
};

int main()
{
/* Following is Valid */
A *ptr = new A;
ptr->fun();
ptr = NULL // make ptr NULL to make sure that things are not accessed using
ptr.

/* And following is Invalid: Undefined Behavior */


A a;
a.fun();

getchar();
return 0;
}

2) Una vez que se hace esto, no se debe acceder a ningn miembro del objeto eliminado
despus de la eliminacin.
#include<iostream>
using namespace std;

class A
{
int x;
public:
A() { x = 0;}
void fun() {
delete this;

/* Invalid: Undefined Behavior */


cout<<x;
}
};

Lo mejor es no eliminar esto en absoluto.

Constructor y Destructor

Constructores en C ++
Qu es constructor?
Un constructor es una funcin miembro de una clase que inicializa objetos de una clase.
A continuacin se muestra un ejemplo sencillo para demostrar constructores en C ++.
#include<iostream>
using namespace std;

class Point
{
private:
int x, y;
public:
/***Constructor****/
Point(int x1, int y1) { x = x1; y = y1; }

int getX() { return x; }


int getY() { return y; }
};

int main()
{
Point p1(10, 15); // constructor is called here

// Let us access values assigned by constructor


cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();

return 0;
}

Salida:
P1.x = 10, p1.y = 15

Cmo los constructores son diferentes de una funcin de miembro normal?


Un constructor es diferente de las funciones normales de las siguientes maneras:
(I) El constructor tiene el mismo nombre que la propia clase
(Ii) Los constructores no tienen tipo de retorno
(Iii) Un constructor se llama automticamente cuando se crea un objeto.
(Iv) Si no especificamos un constructor, el compilador C ++ genera un constructor por defecto
para nosotros (no espera parmetros y tiene un cuerpo vaco).

Podemos tener ms de un constructor en una clase?


Podemos tener ms de un constructor en una clase, siempre que cada uno tenga una lista de
argumentos diferente.
#include<iostream>
using namespace std;
class Point
{
private:
int x, y;
public:
// Two constructors
Point(int x1, int y1) { x = x1; y = y1; }
Point() {x = 0; y = 0; }

int getX() { return x; }


int getY() { return y; }
};

int main()
{
Point p1(10, 15); // first constructor is called here
Point p2; // Second constructor is called here

// Let us access values assigned by constructors


cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();
cout << "\np2.x = " << p2.getX() << ", p2.y = " << p2.getY();

return 0;
}

Salida:
P1.x = 10, p1.y = 15
P2.x = 0, p2.y = 0
Usted puede tener gusto de tomar un concurso en constructores en C ++ .
Pronto cubriremos ms sobre constructores en C ++.

Copiar Constructor en C ++
Hemos discutido la introduccin a los constructores en C ++ . En este post, se discute el
constructor de copia.
Qu es un constructor de copia?
Un constructor de copia es una funcin miembro que inicializa un objeto utilizando otro objeto
de la misma clase.Un constructor de copia tiene el siguiente prototipo de funcin general:
ClassName (const ClassName & old_obj);
A continuacin se muestra un ejemplo sencillo de constructor de copia.
#include<iostream>
using namespace std;

class Point
{
private:
int x, y;
public:
Point(int x1, int y1) { x = x1; y = y1; }

// Copy constructor
Point(const Point &p2) {x = p2.x; y = p2.y; }

int getX() { return x; }


int getY() { return y; }
};
int main()
{
Point p1(10, 15); // Normal constructor is called here
Point p2 = p1; // Copy constructor is called here

// Let us access values assigned by constructors


cout << "p1.x = " << p1.getX() << ", p1.y = " << p1.getY();
cout << "\np2.x = " << p2.getX() << ", p2.y = " << p2.getY();

return 0;
}

Salida:
P1.x = 10, p1.y = 15
P2.x = 10, p2.y = 15

Cundo se llama al constructor de copia?


En C ++, un Copy Constructor puede ser llamado en los siguientes casos:
1. Cuando un objeto de la clase es devuelto por valor.
2. Cuando un objeto de la clase se pasa (a una funcin) por valor como un argumento.
3. Cuando un objeto se construye sobre la base de otro objeto de la misma clase.
4. Cuando el compilador genera un objeto temporal.
Sin embargo, no se garantiza que un constructor de copia se llame en todos estos casos, ya
que el estndar C ++ permite al compilador optimizar la copia en determinados casos, por
ejemplo, la optimizacin del valor de retorno (a veces denominada RVO) .

Cundo se necesita el constructor de copia definido por el usuario?


Si no definimos nuestro propio constructor de copia, el compilador de C ++ crea un constructor
de copia predeterminado para cada clase que hace una copia de miembro sabio entre
objetos. El constructor de copia creado por el compilador funciona bien en
general. Necesitamos definir nuestro propio constructor de copia solo si un objeto tiene
punteros o cualquier asignacin de tiempo de ejecucin de recurso como manejador de
archivo, una conexin de red ... etc.
El constructor predeterminado hace slo copia superficial.

Fuente de la imagen: http://docs.roguewave.com/sourcepro/11.1/html/toolsug/6-4.html


La copia profunda slo es posible con el constructor de copia definido
por el usuario. En el constructor de copia definido por el usuario, nos aseguramos de que
los punteros (o referencias) del objeto copiado apunten a nuevas ubicaciones de memoria.
Fuente de la imagen: http://docs.roguewave.com/sourcepro/11.1/html/toolsug/6-4.html
Constructor de copia vs Operador de asignacin
Cul de las siguientes dos sentencias llama constructor de copia y cul llama al operador de
asignacin?
MyClass t1, t2;
MyClass t3 = t1; // ----> (1)
t2 = t1; // -----> (2)
Run on IDE
El constructor de copia se llama cuando se crea un objeto nuevo a partir de un objeto existente,
como una copia del objeto existente. El operador de asignacin se llama cuando un objeto ya
inicializado se le asigna un nuevo valor a otro objeto existente. En el ejemplo anterior (1) llama
constrictor de copia y (2) operador de asignacin de llamadas. Vea esto para ms detalles.
Escribir una clase de ejemplo donde se necesita el constructor de copia?
A continuacin, se muestra un programa completo de C ++ para demostrar el uso del
constructor Copy. En la siguiente clase String, debemos escribir copy constructor.
#include<iostream>
#include<cstring>
using namespace std;

class String
{
private:
char *s;
int size;
public:
String(const char *str = NULL); // constructor
~String() { delete [] s; }// destructor
String(const String&); // copy constructor
void print() { cout << s << endl; } // Function to print string
void change(const char *); // Function to change
};

String::String(const char *str)


{
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}

void String::change(const char *str)


{
delete [] s;
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}

String::String(const String& old_str)


{
size = old_str.size;
s = new char[size+1];
strcpy(s, old_str.s);
}

int main()
{
String str1("GeeksQuiz");
String str2 = str1;

str1.print(); // what is printed ?


str2.print();

str2.change("GeeksforGeeks");

str1.print(); // what is printed now ?


str2.print();
return 0;
}

Salida:
GeeksQuiz
GeeksQuiz
GeeksQuiz
GeeksforGeeks
Cul sera el problema si eliminamos el constructor de copia del cdigo
anterior?
Si eliminamos el constructor de copia del programa anterior, no obtenemos el resultado
esperado. Los cambios realizados en str2 reflejan tambin en str1 lo que nunca se espera.
#include<iostream>
#include<cstring>
using namespace std;

class String
{
private:
char *s;
int size;
public:
String(const char *str = NULL); // constructor
~String() { delete [] s; }// destructor
void print() { cout << s << endl; }
void change(const char *); // Function to change
};

String::String(const char *str)


{
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}

void String::change(const char *str)


{
delete [] s;
size = strlen(str);
s = new char[size+1];
strcpy(s, str);
}

int main()
{
String str1("GeeksQuiz");
String str2 = str1;

str1.print(); // what is printed ?


str2.print();

str2.change("GeeksforGeeks");

str1.print(); // what is printed now ?


str2.print();
return 0;
}

Salida:
GeeksQuiz
GeeksQuiz
GeeksforGeeks
GeeksforGeeks
Podemos hacer que el constructor de copia sea privado?
S, un constructor de copia puede hacerse privado. Cuando hacemos un constructor de copia
privado en una clase, los objetos de esa clase se vuelven no copiadores. Esto es
particularmente til cuando nuestra clase tiene punteros o recursos asignados
dinmicamente. En estas situaciones, podemos escribir nuestro propio constructor de copia
como anteriormente Ejemplo de cadena, o hacer un constructor de copia privada para que los
usuarios obtengan errores de compilador en lugar de sorpresas en tiempo de ejecucin.
Por qu el argumento a un constructor de copia debe ser pasado como
una referencia?
Un constructor de copia se llama cuando un objeto se pasa por valor. El constructor de copia
es una funcin.Por lo tanto, si pasamos el argumento por valor en un constructor de copia, se
hara una llamada al constructor de copia para llamar al constructor de copia que se convierte
en una cadena de llamadas que no termina. Por lo tanto, el compilador no permite que los
parmetros pasen por valor.
Por qu el argumento a un constructor de copia debe ser const?
Ver http://www.geeksforgeeks.org/copy-constructor-argument-const/

Este artculo es aportado por Shubham Agrawal . Si te gusta GeeksforGeeks y te gustara


contribuir, tambin puedes enviar tu artculo a contribution@geeksforgeeks.org. Vea su artculo
apareciendo en la pgina principal de GeeksforGeeks y ayude a otros Geeks.

Destructores en C ++
Qu es destructor?
Destructor es una funcin miembro que destruye o elimina un objeto.
Cundo se llama al destructor?
Una funcin destructor se llama automticamente cuando el objeto sale del alcance:
(1) la funcin termina
(2) el programa termina
(3) un bloque que contiene las variables locales termina
(4) un operador de eliminacin se llama
Cmo los destructores son diferentes de una funcin de miembro normal?
Los destructores tienen el mismo nombre que la clase precedida por un tilde (~)
Los destructores no toman ningn argumento y no devuelven nada
class String
{
private:
char *s;
int size;
public:
String(char *); // constructor
~String(); // destructor
};

String::String(char *c)
{
size = strlen(c);
s = new char[size+1];
strcpy(s,c);
}

String::~String()
{
delete []s;
}

Puede haber ms de un destructor en una clase?


No, slo puede haber un destructor en una clase con nombre de clase precedido por ~, sin
parmetros y sin tipo de retorno.
Cundo necesitamos escribir un destructor definido por el usuario?
Si no escribimos nuestro propio destructor en clase, el compilador crea un destructor por
defecto para nosotros.El destructor predeterminado funciona correctamente a menos que
hayamos asignado dinmicamente memoria o puntero en clase. Cuando una clase contiene un
puntero a memoria asignada en clase, deberamos escribir un destructor para liberar memoria
antes de que la clase sea destruida. Esto se debe hacer para evitar fugas de memoria.
Puede un destructor ser virtual?
S, de hecho, siempre es una buena idea hacer destructores virtuales en la clase base cuando
tenemos una funcin virtual. Ver destructor virtual para ms detalles.

El compilador crea el constructor por defecto


cuando escribimos nuestro propio?
En C ++, el compilador por defecto crea el constructor por defecto para cada clase. Pero, si
definimos nuestro propio constructor, el compilador no crea el constructor por defecto.
Por ejemplo, el programa 1 compila sin ningn error, pero la compilacin del programa 2 falla
con el error "ninguna funcin de coincidencia para la llamada a` myInteger :: myInteger () '"
Programa 1
#include<iostream>

using namespace std;

class myInteger
{
private:
int value;

//...other things in class


};
int main()
{
myInteger I1;
getchar();
return 0;
}

Programa 2
#include<iostream>

using namespace std;

class myInteger
{
private:
int value;
public:
myInteger(int v) // parametrized constructor
{ value = v; }

//...other things in class


};

int main()
{
myInteger I1;
getchar();
return 0;
}

Cundo deberamos escribir nuestro propio


constructor de copias?
No escribir un constructor de copia si las copias poco profundas son correctas: En C + +, si un
objeto no tiene punteros o cualquier asignacin de tiempo de ejecucin de recurso como
manejador de archivo, una conexin de red .. etc, una copia superficial es probablemente
suficiente. Por lo tanto, el constructor de copia predeterminado, el operador de asignacin
predeterminado y el destructor predeterminado estn bien y no es necesario que escriba el
suyo propio.

Cundo se llama al constructor de copia?


En C ++, un Copy Constructor puede ser llamado en los siguientes casos:
1. Cuando un objeto de la clase es devuelto por valor.
2. Cuando un objeto de la clase se pasa (a una funcin) por valor como un argumento.
3. Cuando un objeto se construye sobre la base de otro objeto de la misma clase.
4. Cuando el compilador genera un objeto temporal.
Sin embargo, no se garantiza que un constructor de copia se llame en todos estos casos, ya
que el C ++ Standard permite al compilador optimizar la copia en ciertos casos, por ejemplo,
la optimizacin del valor de retorno (a veces denominada RVO).
Inicializacin de miembros de datos
En C ++, las variables de clase se inicializan en el mismo orden en que
aparecen en la declaracin de clase.
Considere el siguiente cdigo.
#include<iostream>

using namespace std;

class Test {
private:
int y;
int x;
public:
Test() : x(10), y(x + 10) {}
void print();
};

void Test::print()
{
cout<<"x = "<<x<<" y = "<<y;
}

int main()
{
Test t;
t.print();
getchar();
return 0;
}

El programa imprime el valor correcto de x, pero un valor de basura para y,


porque y se inicializa antes de x como aparece antes en la declaracin de
clase.
Por lo tanto, una de las dos versiones siguientes se puede utilizar para evitar el
problema en el cdigo anterior.
// First: Change the order of declaration.
class Test {
private:
int x;
int y;
public:
Test() : x(10), y(x + 10) {}
void print();
};

// Second: Change the order of initialization.


class Test {
private:
int y;
int x;
public:
Test() : x(y-10), y(20) {}
void print();
};
Cundo usamos la lista de inicializadores en C
++?
La lista de inicializacin se utiliza para inicializar miembros de datos de una clase. La lista de
miembros a inicializar se indica con el constructor como una lista separada por comas seguida
de dos puntos. A continuacin se muestra un ejemplo que utiliza la lista de inicializadores para
inicializar xyy de la clase Point.
#include<iostream>
using namespace std;

class Point {
private:
int x;
int y;
public:
Point(int i = 0, int j = 0):x(i), y(j) {}
/* The above use of Initializer list is optional as the
constructor can also be written as:
Point(int i = 0, int j = 0) {
x = i;
y = j;
}
*/

int getX() const {return x;}


int getY() const {return y;}
};

int main() {
Point t1(10, 15);
cout<<"x = "<<t1.getX()<<", ";
cout<<"y = "<<t1.getY();
return 0;
}

/* OUTPUT:
x = 10, y = 15
*/

El cdigo anterior es slo un ejemplo de sintaxis de la lista Inicializador. En el cdigo anterior, x


e y tambin pueden iniciarse fcilmente dentro del constructor. Pero hay situaciones en las que
la inicializacin de los miembros de datos dentro del constructor no funciona y se debe utilizar
la Lista de inicializacin. Los siguientes son los casos:
1) Para la inicializacin de miembros de datos const no estticos:
Los miembros de datos const deben inicializarse mediante la lista de inicializadores. En el
ejemplo siguiente, "t" es un miembro de datos const de la clase Test y se inicializa mediante la
Lista de inicializacin.
#include<iostream>
using namespace std;

class Test {
const int t;
public:
Test(int t):t(t) {} //Initializer list must be used
int getT() { return t; }
};
int main() {
Test t1(10);
cout<<t1.getT();
return 0;
}

/* OUTPUT:
10
*/

2) Para la inicializacin de los miembros de referencia:


Los miembros de referencia se deben inicializar mediante la lista de inicializacin. En el
ejemplo siguiente, "t" es un miembro de referencia de la clase Test y se inicializa mediante
Lista de inicializacin.
// Initialization of reference data members
#include<iostream>
using namespace std;

class Test {
int &t;
public:
Test(int &t):t(t) {} //Initializer list must be used
int getT() { return t; }
};

int main() {
int x = 20;
Test t1(x);
cout<<t1.getT()<<endl;
x = 30;
cout<<t1.getT()<<endl;
return 0;
}
/* OUTPUT:
20
30
*/

3) Para la inicializacin de objetos miembro que no tienen constructor por defecto:


En el ejemplo siguiente, un objeto "a" de clase "A" es miembro de datos de clase "B" y "A" no
tiene constructor predeterminado. La lista de inicializacin debe utilizarse para inicializar "a".
#include <iostream>
using namespace std;

class A {
int i;
public:
A(int );
};

A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}

// Class B contains object of A


class B {
A a;
public:
B(int );
};

B::B(int x):a(x) { //Initializer list must be used


cout << "B's Constructor called";
}

int main() {
B obj(10);
return 0;
}
/* OUTPUT:
A's Constructor called: Value of i: 10
B's Constructor called
*/

Si la clase A tena constructores por defecto y parametrizados, entonces la Lista de


Inicializador no es obligatoria si queremos inicializar "a" usando el constructor por defecto, pero
es necesario inicializar "a" usando el constructor parametrizado.

4) Para la inicializacin de los miembros de la clase base: Al igual que el punto 3, el


constructor parametrizado de la clase base slo puede ser llamado usando la Lista de
inicializacin.
#include <iostream>
using namespace std;

class A {
int i;
public:
A(int );
};

A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}

// Class B is derived from A


class B: A {
public:
B(int );
};

B::B(int x):A(x) { //Initializer list must be used


cout << "B's Constructor called";
}

int main() {
B obj(10);
return 0;
}

5) Cuando el nombre del parmetro del constructor es el mismo que el miembro de


datos
Si el nombre del parmetro constructor es el mismo que el nombre del miembro de datos,
entonces el miembro de datos debe ser inicializado utilizando este puntero o la Lista de
inicializacin. En el ejemplo siguiente, nombre de miembro y nombre de parmetro para A () es
"i".
#include <iostream>
using namespace std;

class A {
int i;
public:
A(int );
int getI() const { return i; }
};

A::A(int i):i(i) { } // Either Initializer list or this pointer must be used


/* The above constructor can also be written as
A::A(int i) {
this->i = i;
}
*/

int main() {
A a(10);
cout<<a.getI();
return 0;
}
/* OUTPUT:
10
*/

6) Por razones de rendimiento:


Es mejor inicializar todas las variables de clase en la lista de inicializadores en lugar de asignar
valores dentro del cuerpo. Considere el siguiente ejemplo:
// Without Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
variable = a;
}
};

Aqu el compilador sigue los siguientes pasos para crear un objeto de tipo MyClass
1. El constructor de tipo se llama primero para "a".
2. El operador de asignacin de "Tipo" se llama dentro del cuerpo del constructor MyClass ()
para asignar
Variable = a;

3. Y finalmente, finalmente, el destructor de "Tipo" se llama "a" ya que sale fuera del alcance.
Ahora considere el mismo cdigo con MyClass () constructor con Lista de Inicializador
// With Initializer List
class MyClass {
Type variable;
public:
MyClass(Type a):variable(a) { // Assume that Type is an already
// declared class and it has appropriate
// constructors and operators
}
};
Con la lista de inicializacin, los siguientes pasos son seguidos por el compilador:
1. El constructor de copia de la clase "Tipo" se llama para inicializar: variable (a). Los
argumentos en la lista de inicializadores se usan para copiar la construccin "variable"
directamente.
2. Destructor de "Tipo" es llamado para "a" ya que sale fuera del alcance.
Como podemos ver en este ejemplo si utilizamos asignacin dentro del cuerpo del constructor
hay tres llamadas de funcin: constructor + destructor + una llamada de operador de
asignacin de adicin. Y si usamos la lista de inicializadores slo hay dos llamadas de funcin:
constructor de copia + llamada destructor. Vea este post para ver un ejemplo en este punto.
Esta penalidad de asignacin ser mucho ms en aplicaciones "reales" donde habr muchas
de tales variables.Gracias a ptr para agregar este punto.

C ++ Internals | Constructores por defecto | Serie 1


Un constructor sin argumentos o con valor predeterminado para cada argumento , se dice
que es el constructor por defecto . Cul es la importancia del constructor por
defecto? Se generar el cdigo para cada constructor predeterminado? Habr algn cdigo
insertado por el compilador para el usuario implementado por defecto constructor detrs de las
escenas?
El compilador implicitamente declarar el constructor por defecto si no lo proporciona el
programador, lo definircuando lo necesite. El constructor por defecto definido por el
compilador es necesario para realizar una inicializacin segura de los internos de la clase. No
tocar a los miembros de datos o tipos de datos antiguos (agregados como matriz, estructuras,
etc ...). Sin embargo, el compilador genera cdigo para el constructor predeterminado en
funcin de la situacin.
Considere una clase derivada de otra clase con constructor predeterminado o una clase que
contenga otro objeto de clase con el constructor predeterminado. El compilador necesita
insertar cdigo para llamar a los constructores predeterminados de la clase base / objeto
incrustado.

#include <iostream>
using namespace std;

class Base
{
public:
// compiler "declares" constructor
};

class A
{
public:
// User defined constructor
A()
{
cout << "A Constructor" << endl;
}

// uninitialized
int size;
};

class B : public A
{
// compiler defines default constructor of B, and
// inserts stub to call A constructor
// compiler won't initialize any data of A
};

class C : public A
{
public:
C()
{
// User defined default constructor of C
// Compiler inserts stub to call A's construtor
cout << "B Constructor" << endl;

// compiler won't initialize any data of A


}
};

class D
{
public:
D()
{
// User defined default constructor of D
// a - constructor to be called, compiler inserts
// stub to call A constructor
cout << "D Constructor" << endl;

// compiler won't initialize any data of 'a'


}

private:
A a;
};

int main()
{
Base base;

B b;
C c;
D d;

return 0;
}
Existen diferentes escenarios en los que el compilador necesita insertar cdigo para garantizar
la inicializacin necesaria segn el requisito de idioma. Los tendremos en los prximos
posts. Nuestro objetivo es estar al tanto de C ++ internos, no utilizarlos incorrectamente.

Destructor privado
Predecir el resultado de los siguientes programas.
#include <iostream>
using namespace std;

class Test
{
private:
~Test() {}
};
int main()
{ }
El programa anterior se compila y funciona bien. No es un error de compilador crear
destructores privados. Qu dices sobre el programa siguiente.
#include <iostream>
using namespace std;

class Test
{
private:
~Test() {}
};
int main()
{
Test t;
}

El programa anterior falla en la compilacin. El compilador advierte que la variable local 't' no se
puede destruir porque el destructor es privado. Qu pasa con el siguiente programa?
#include <iostream>
using namespace std;

class Test
{
private:
~Test() {}
};
int main()
{
Test *t;
}

El programa anterior funciona bien. No hay ningn objeto que se est construyendo, el
programa slo crea un puntero de tipo "Test *", por lo que nada se destruye. Qu pasa con el
siguiente programa?
#include <iostream>
using namespace std;

class Test
{
private:
~Test() {}
};
int main()
{
Test *t = new Test;
}

El programa anterior tambin funciona bien. Cuando se crea algo mediante asignacin de
memoria dinmica, es responsabilidad del programador eliminarlo. As que el compilador no se
molesta.
El siguiente programa falla en la compilacin. Cuando llamamos delete, se llama a desturctor.
#include <iostream>
using namespace std;

class Test
{
private:
~Test() {}
};
int main()
{
Test *t = new Test;
delete t;
}

Hemos notado en los programas anteriores, cuando una clase tiene destructur privado, slo los
objetos dinmicos de esa clase se pueden crear. A continuacin se muestra una forma de crear
clases con destructores privados y tener una funcin como amigo de la clase. La funcin slo
puede eliminar los objetos.
#include <iostream>

// A class with private destuctor


class Test
{
private:
~Test() {}
friend void destructTest(Test* );
};

// Only this function can destruct objects of Test


void destructTest(Test* ptr)
{
delete ptr;
}

int main()
{
// create an object
Test *ptr = new Test;

// destruct the object


destructTest (ptr);

return 0;
}

Cul es el uso del destructor privado?


Siempre que queramos controlar la destruccin de objetos de una clase, hacemos que el
destructor sea privado.Para objetos creados dinmicamente, puede suceder que pase un
puntero al objeto a una funcin y la funcin elimine el objeto. Si el objeto es referido despus
de la llamada de funcin, la referencia se pondr colgando.Vea esto para ms detalles.

Jugar con destructores en C ++


Predice la salida del siguiente fragmento de cdigo.
#include <iostream>
using namespace std;

int i;

class A
{
public:
~A()
{
i=10;
}
};
int foo()
{
i=3;
A ob;
return i;
}

int main()
{
cout << "i = " << foo() << endl;
return 0;
}

La salida del programa anterior es "i = 3".


Por qu la salida es i = 3 y no 10?
Al regresar de una funcin, el destructor es el ltimo mtodo a ejecutar. El destructor para el
objeto "ob" se llama despus de que el valor de i se copie en el valor de retorno de la
funcin. As, antes de que el destructor pudiera cambiar el valor de i a 10, el valor actual de i se
copia y por lo tanto la salida es i = 3.
Cmo hacer que el programa emita "i = 10"?
A continuacin se presentan dos formas de devolver el valor actualizado:
1) Retorno por referencia:
Dado que la referencia da el valor l de la variable, mediante el retorno por referencia, el
programa emitir "i = 10".
#include <iostream>
using namespace std;

int i;

class A
{
public:
~A()
{
i = 10;
}
};

int& foo()
{
i = 3;
A ob;
return i;
}

int main()
{
cout << "i = " << foo() << endl;
return 0;
}

La funcin foo () devuelve el valor l de la variable i. Por lo tanto, la direccin de i se copiar en


el valor de retorno. Dado que, las referencias son automticamente dereferen. Producir "i =
10".

2. Cree el objeto ob en un mbito de bloque


#include <iostream>
using namespace std;
int i;

class A
{
public:
~A()
{
i = 10;
}
};

int foo()
{
i = 3;
{
A ob;
}
return i;
}

int main()
{
cout << "i = " << foo() << endl;
return 0;
}

Dado que el objeto ob se crea en el mbito del bloque, el destructor del objeto ser llamado
despus de que el bloque termine, cambiando el valor de i a 10. Finalmente, 10 se copiar al
valor de retorno.

Copiar elisin en C ++
Elisin de copia (o omisin de copia) es una tcnica de optimizacin de compilador que evita la
copia innecesaria de objetos. Ahora un da, casi todos los compiladores lo utilizan. Vamos a
entenderlo con la ayuda de un ejemplo.
#include <iostream>
using namespace std;

class B
{
public:
B(const char* str = "\0") //default constructor
{
cout << "Constructor called" << endl;
}

B(const B &b) //copy constructor


{
cout << "Copy constructor called" << endl;
}
};

int main()
{
B ob = "copy me";
return 0;
}
La salida del programa anterior es:
Constructor llamado

Por qu no se llama al constructor de copia?


Segn la teora, cuando se construye el objeto "ob", se utiliza un constructor de argumentos
para convertir "copiarme" en un objeto temporal y ese objeto temporal se copia en el objeto
"ob". As que la declaracin
B ob = "copiarme";

Debe ser desglosado por el compilador como


B ob = B ("copiarme");

Sin embargo, la mayora de los compiladores de C ++ evitan tales overheads de crear un


objeto temporal y despus copiarlo.
Los compiladores modernos desglosan la declaracin
B ob = "copiarme"; // copia de inicializacin
como
B ob ("copiarme"); // inicializacin directa
Y por lo tanto elidir la llamada al constructor de copia.

Sin embargo, si todava queremos asegurarnos de que el compilador no elide la llamada a


copiar constructor [disable the copy elision], podemos compilar el programa usando la opcin "-
fno-elide-constructors" con g ++ y ver la salida como siguiente:
Aashish @ aashish-ThinkPad-SL400: ~ $ g ++ copy_elision.cpp -fno-elide-
constructores
Aashish @ aashish-ThinkPad-SL400: ~ $ ./a.out
Constructor llamado
Copiar constructor llamado

Si se utiliza la opcin "-fno-elide-constructors", se invoca al primer constructor por defecto para


crear un objeto temporal, entonces se llama al constructor copy para copiar el objeto temporal a
ob.

Constructor predeterminado de C ++ | Tipos


incorporados
Predice la salida del siguiente programa?
#include <iostream>
using namespace std;

int main() {

cout << int() << endl;


return 0;
}

Un constructor sin argumentos o con valores por defecto para cada argumento , se trata
como constructor por defecto . Ser llamado por el compilador cuando se necesite (cdigo
precisamente se generar para el constructor por defecto basado en la necesidad).
C ++ permite que incluso el tipo incorporado (tipos primitivos) tenga constructores
predeterminados. La funcin style cast int () es anloga a casting 0 al tipo requerido. El
programa imprime 0 en la consola.
El contenido inicial del artculo desencaden muchas discusiones, dado a continuacin es la
consolidacin.
Vale la pena conocer la semntica de referencia vs. valor en C ++ y el concepto de Plai n tipos
de datos antiguos. Desde Wiki, los tipos primitivos y los tipos POD no tienen operador de
asignacin de copia definido por el usuario, ningn destructor definido por el usuario y ningn
miembro de datos no estticos que no sean PODs.Adems, una clase POD debe ser un
agregado, lo que significa que no tiene constructores declarados por el usuario, no hay datos
privados no protegidos, no hay clases base y no hay funciones virtuales.
Un extracto (de una nota de correo) del creador de C ++, "Creo que mezclas" llamadas
constructoras reales "con conceptualmente tener un constructor. Se considera que los tipos
incorporados tienen constructores ".
El fragmento de cdigo arriba mencionado int () se considera que tiene conceptualmente
constructor. Sin embargo, no habr ningn cdigo generado para hacer una llamada
de constructor explcita . Pero cuando observamos la salida del ensamblaje, se generar
cdigo para inicializar el identificador utilizando la semntica del valor. Para ms detalles,
consulte la seccin 8.5 de este documento.
Gracias a Prasoon Saurav por haber iniciado la discusin, proporcionando varias referencias y
corrigiendo lagunas en mi entendimiento.
Contribucin de Venki . Por favor escriba comentarios si encuentra algo incorrecto, o si desea
compartir ms informacin sobre el tema discutido anteriormente.

Cundo el compilador crea constructores por


defecto y copia en C ++?
En C ++, el compilador crea un constructor por defecto si no definimos nuestro propio
constructor (Ver esto ). El compilador creado por el compilador tiene un cuerpo vaco, es decir,
no asigna valores predeterminados a los miembros de los datos ( En java, los constructores
predeterminados asignan valores predeterminados ).
Compiler tambin crea un constructor de copia si no escribimos nuestro propio constructor de
copia. A diferencia del constructor predeterminado, el cuerpo del constructor de copia creado
por el compilador no est vaco, copia todos los miembros de datos del objeto pasado al objeto
que se est creando.
Qu sucede cuando escribimos slo un constructor de copia - el
compilador crea el constructor por defecto?
El compilador no crea un constructor por defecto si escribimos cualquier constructor incluso si
es constructor de copia. Por ejemplo, el programa siguiente no compila.
#include <iostream>
using namespace std;

class Point
{
int x, y;
public:
Point(const Point &p) { x = p.x; y = p.y; }
};

int main()
{
Point p1; // COMPILER ERROR
Point p2 = p1;
return 0;
}

Salida:
ERROR COMPILER: ninguna funcin de coincidencia para la llamada a 'Point :: Point ()

Qu pasa con inversa - lo que sucede cuando escribimos un constructor


normal y no escribimos un constructor de copia?
Reverse no es cierto. Compiler crea un constructor de copia si no escribimos el nuestro. El
compilador lo crea incluso si hemos escrito otros constructores en clase. Por ejemplo, el
siguiente programa funciona bien.
#include <iostream>
using namespace std;

class Point
{
int x, y;
public:
Point(int i, int j) { x = 10; y = 20; }
int getX() { return x; }
int getY() { return y; }
};

int main()
{
Point p1(10, 20);
Point p2 = p1; // This compiles fine
cout << "x = " << p2.getX() << " y = " << p2.getY();
return 0;
}

Salida:
X = 10 y = 20

As que tenemos que escribir el constructor de copia slo cuando tenemos punteros o la
asignacin de tiempo de ejecucin de recursos como manejador de archivos, una conexin de
red, etc.

Por qu el argumento del constructor de copia


debe ser const en C ++?
Cuando creamos nuestro propio constructor de copia, pasamos un objeto por referencia y
generalmente lo pasamos como una referencia const.
Una razn para pasar referencia const es, deberamos usar const en C ++ siempre que sea
posible para que los objetos no se modifiquen accidentalmente. Esta es una buena razn para
pasar referencia como const, pero hay ms. Por ejemplo, predecir la salida del siguiente
programa de C ++. Suponga que la elisin de copia no esrealizada por el compilador.
#include<iostream>
using namespace std;

class Test
{
/* Class data members */
public:
Test(Test &t) { /* Copy data members from t*/}
Test() { /* Initialize data members */ }
};

Test fun()
{
cout << "fun() Called\n";
Test t;
return t;
}

int main()
{
Test t1;
Test t2 = fun();
return 0;
}

Salida:
Error del compilador en la lnea "Test t2 = fun ();"

El programa se ve bien a primera vista, pero tiene error de compilador. Si agregamos const en
el constructor de copia, el programa funciona bien, es decir, cambiamos el constructor de copia
a siguiente.
Test(const Test &t) { cout << "Copy Constructor Called\n"; }

O si cambiamos la lnea "Prueba t2 = diversin ();" siguiendo dos lneas, entonces tambin el
programa funciona bien.
Test t2;
t2 = fun();

La funcin fun () devuelve por valor. As que el compilador crea un objeto temporal que se
copia a t2 usando el constructor de copia en el programa original (El objeto temporal se pasa
como argumento al constructor de copia). La razn del error del compilador es que los objetos
temporales creados por el compilador no pueden enlazarse a referencias no const y el
programa original intenta hacerlo. No tiene sentido modificar objetos temporales creados por el
compilador, ya que pueden morir en cualquier momento.

C ++ avanzado | Constructor Virtual


Podemos hacer un constructor de clase virtual en C ++ para crear objetos polimrficos? No. C
++ siendo esttica mecanografiada (el propsito de RTTI es diferente) de lenguaje, no tiene
sentido para el compilador C ++ crear un objeto de forma polimrfica. El compilador debe tener
en cuenta el tipo de clase para crear el objeto.En otras palabras, qu tipo de objeto a crear es
una decisin de tiempo de compilacin desde la perspectiva del compilador de C ++. Si
hacemos constructor virtual, el compilador seala un error. De hecho, excepto en lnea , no se
permite ninguna otra palabra clave en la declaracin del constructor.
En escenarios prcticos necesitaramos crear un objeto de clase derivado en una jerarqua de
clases basada en alguna entrada. En otras palabras, la creacin de objetos y el tipo de objeto
estn estrechamente acoplados, lo que obliga a las modificaciones a extenderse. El objetivo
del constructor virtual es desacoplar la creacin de objetos de su tipo .
Cmo podemos crear el tipo de objeto requerido en tiempo de ejecucin? Por ejemplo,
consulte el siguiente programa de ejemplo.
#include <iostream>
using namespace std;

//// LIBRARY START


class Base
{
public:

Base() { }

virtual // Ensures to invoke actual object destructor


~Base() { }

// An interface
virtual void DisplayAction() = 0;
};

class Derived1 : public Base


{
public:
Derived1()
{
cout << "Derived1 created" << endl;
}

~Derived1()
{
cout << "Derived1 destroyed" << endl;
}

void DisplayAction()
{
cout << "Action from Derived1" << endl;
}
};

class Derived2 : public Base


{
public:
Derived2()
{
cout << "Derived2 created" << endl;
}

~Derived2()
{
cout << "Derived2 destroyed" << endl;
}

void DisplayAction()
{
cout << "Action from Derived2" << endl;
}
};

//// LIBRARY END

class User
{
public:

// Creates Drived1
User() : pBase(0)
{
// What if Derived2 is required? - Add an if-else ladder (see next
sample)
pBase = new Derived1();
}

~User()
{
if( pBase )
{
delete pBase;
pBase = 0;
}
}
// Delegates to actual object
void Action()
{
pBase->DisplayAction();
}

private:
Base *pBase;
};

int main()
{
User *user = new User();

// Need Derived1 functionality only


user->Action();

delete user;
}

En el ejemplo anterior, suponga que la jerarqua Base , Derived1 y Derived2 forman parte del
cdigo de biblioteca. El usuario de clase es la clase de utilidad que intenta hacer uso de la
jerarqua. La funcin principal es consumir la funcionalidad de jerarqua de base a travs de
la clase de usuario .
El constructor de clase User crea el objeto Derived1 , siempre. Si el consumidor
del Usuario (el principal en nuestro caso) necesita la funcionalidad Derived2,
el Usuario necesita crear " new Derived2 () " y obliga a la recompilacin. Recompilar es una
mala forma de diseo, por lo que podemos optar por el siguiente enfoque.
Antes de entrar en detalles, respondamos , quin dictar para crear cualquiera de
los objetos Derived1 oDerived2 ? Claramente, es el consumidor de la clase de usuario . La
clase User puede hacer uso de la escalera if-else para crear Derived1 o Derived2 , como se
muestra en el siguiente ejemplo,
#include <iostream>
using namespace std;

//// LIBRARY START


class Base
{
public:
Base() { }

virtual // Ensures to invoke actual object destructor


~Base() { }

// An interface
virtual void DisplayAction() = 0;
};

class Derived1 : public Base


{
public:
Derived1()
{
cout << "Derived1 created" << endl;
}

~Derived1()
{
cout << "Derived1 destroyed" << endl;
}

void DisplayAction()
{
cout << "Action from Derived1" << endl;
}
};

class Derived2 : public Base


{
public:
Derived2()
{
cout << "Derived2 created" << endl;
}

~Derived2()
{
cout << "Derived2 destroyed" << endl;
}

void DisplayAction()
{
cout << "Action from Derived2" << endl;
}
};

//// LIBRARY END

class User
{
public:

// Creates Derived1 or Derived2 based on input


User() : pBase(0)
{
int input; // ID to distinguish between
// Derived1 and Derived2

cout << "Enter ID (1 or 2): ";


cin >> input;

while( (input != 1) && (input != 2) )


{
cout << "Enter ID (1 or 2 only): ";
cin >> input;
}

if( input == 1 )
{
pBase = new Derived1;
}
else
{
pBase = new Derived2;
}

// What if Derived3 being added to the class hierarchy?


}

~User()
{
if( pBase )
{
delete pBase;
pBase = 0;
}
}

// Delegates to actual object


void Action()
{
pBase->DisplayAction();
}

private:
Base *pBase;
};

int main()
{
User *user = new User();

// Need either Derived1 or Derived2 functionality


user->Action();

delete user;
}

El cdigo anterior es * not * abierto para la extensin, un diseo inflexible. En palabras simples,
si la biblioteca actualiza la jerarqua de la clase Base con la nueva clase Derived3. Cmo
puede la clase User crear un objeto Derived3? Una forma es actualizar la escalera if-else que
crea el objeto Derived3 basndose en la nueva ID de entrada 3 como se muestra
a continuacin,
#include <iostream>
using namespace std;

class User
{
public:
User() : pBase(0)
{
// Creates Drived1 or Derived2 based on need

int input; // ID to distinguish between


// Derived1 and Derived2

cout << "Enter ID (1 or 2): ";


cin >> input;

while( (input != 1) && (input != 2) )


{
cout << "Enter ID (1 or 2 only): ";
cin >> input;
}

if( input == 1 )
{
pBase = new Derived1;
}
else if( input == 2 )
{
pBase = new Derived2;
}
else
{
pBase = new Derived3;
}
}

~User()
{
if( pBase )
{
delete pBase;
pBase = 0;
}
}

// Delegates to actual object


void Action()
{
pBase->DisplayAction();
}

private:
Base *pBase;
};

La modificacin anterior obliga a los usuarios de la clase User a recompilar, mal diseo
(inflexible)! Y no cerrar la clase de usuario de otras modificaciones debido a la extensin base.
El problema es con la creacin de objetos. Adicin de una nueva clase a la jerarqua obligando
a los dependientes de la clase User a recompilar. No podemos delegar la accin de crear
objetos a la propia jerarqua de clases oa una funcin que se comporta virtualmente? Al
delegar la creacin de objetos a la jerarqua de clases (oa una funcin esttica) podemos evitar
el estrecho acoplamiento entre la jerarqua de usuario y base .Bastante teora, vea el cdigo
siguiente,
#include <iostream>
using namespace std;

//// LIBRARY START


class Base
{
public:

// The "Virtual Constructor"


static Base *Create(int id);

Base() { }

virtual // Ensures to invoke actual object destructor


~Base() { }

// An interface
virtual void DisplayAction() = 0;
};
class Derived1 : public Base
{
public:
Derived1()
{
cout << "Derived1 created" << endl;
}

~Derived1()
{
cout << "Derived1 destroyed" << endl;
}

void DisplayAction()
{
cout << "Action from Derived1" << endl;
}
};

class Derived2 : public Base


{
public:
Derived2()
{
cout << "Derived2 created" << endl;
}

~Derived2()
{
cout << "Derived2 destroyed" << endl;
}

void DisplayAction()
{
cout << "Action from Derived2" << endl;
}
};

class Derived3 : public Base


{
public:
Derived3()
{
cout << "Derived3 created" << endl;
}

~Derived3()
{
cout << "Derived3 destroyed" << endl;
}

void DisplayAction()
{
cout << "Action from Derived3" << endl;
}
};

// We can also declare "Create" outside Base


// But it is more relevant to limit it's scope to Base
Base *Base::Create(int id)
{
// Just expand the if-else ladder, if new Derived class is created
// User code need not be recompiled to create newly added class objects

if( id == 1 )
{
return new Derived1;
}
else if( id == 2 )
{
return new Derived2;
}
else
{
return new Derived3;
}
}
//// LIBRARY END

//// UTILITY START


class User
{
public:
User() : pBase(0)
{
// Receives an object of Base heirarchy at runtime

int input;

cout << "Enter ID (1, 2 or 3): ";


cin >> input;

while( (input != 1) && (input != 2) && (input != 3) )


{
cout << "Enter ID (1, 2 or 3 only): ";
cin >> input;
}

// Get object from the "Virtual Constructor"


pBase = Base::Create(input);
}

~User()
{
if( pBase )
{
delete pBase;
pBase = 0;
}
}

// Delegates to actual object


void Action()
{
pBase->DisplayAction();
}
private:
Base *pBase;
};

//// UTILITY END

//// Consumer of User (UTILITY) class


int main()
{
User *user = new User();

// Action required on any of Derived objects


user->Action();

delete user;
}

La clase User es independiente de la creacin de objetos. Delega esa responsabilidad


en Base y proporciona una entrada en forma de ID. Si la biblioteca agrega nueva
clase Derived4, el modificador de biblioteca ampliar la escala if-else dentro de Create para
devolver el objeto adecuado. Los consumidores de Usuario no necesitan recompilar su cdigo
debido a la extensin de Base.
Tenga en cuenta que la funcin Crear se utiliza para devolver diferentes tipos de objetos de
clase Base en tiempo de ejecucin. Acta como constructor virtual, tambin conocido
como Mtodo de Fbrica en terminologa de patrones.
El mundo del patrn demuestra maneras diferentes de poner en prctica el concepto
antedicho. Tambin hay algunos posibles problemas de diseo con el cdigo anterior. Nuestro
objetivo es proporcionar algunas ideas sobre la construccin virtual, la creacin de objetos de
forma dinmica sobre la base de algunos aportes.Tenemos excelentes libros dedicados al
tema, el lector interesado puede remitirlos para ms informacin.

C ++ avanzado | Constructor de copia virtual


En el lenguaje del constructor virtual hemos visto la forma de construir un objeto cuyo tipo no
se determina hasta el tiempo de ejecucin. Es posible crear un objeto sin saber su tipo de
clase exacta? El constructor de copia virtual aborda esta pregunta.
A veces podemos necesitar construir un objeto de otro objeto existente. Precisamente el
constructor de copia hace lo mismo. El estado inicial del nuevo objeto se basar en otro estado
de objeto existente. El compilador coloca la llamada al constructor de copia cuando un objeto
est siendo instanciado desde otro objeto. Sin embargo, el compilador necesita informacin de
tipo concreto para invocar el constructor de copia adecuado.
#include <iostream>
using namespace std;

class Base
{
public:
//
};

class Derived : public Base


{
public:
Derived()
{
cout << "Derived created" << endl;
}
Derived(const Derived &rhs)
{
cout << "Derived created by deep copy" << endl;
}

~Derived()
{
cout << "Derived destroyed" << endl;
}
};

int main()
{
Derived s1;

Derived s2 = s1; // Compiler invokes "copy constructor"


// Type of s1 and s2 are concrete to compiler

// How can we create Derived1 or Derived2 object


// from pointer (reference) to Base class pointing Derived object?

return 0;
}

Qu pasa si no podemos decidir de qu tipo de objeto, la construccin de la copia que se


har? Por ejemplo, elconstructor virtual crea un objeto de jerarqua de clases en tiempo de
ejecucin basado en alguna entrada.Cuando queremos copiar construir un objeto desde otro
objeto creado por el constructor virtual, no podemos usar el constructor de copia
habitual. Necesitamos una funcin de clonacin especial que pueda duplicar el objeto en
tiempo de ejecucin.
Por ejemplo, considere una aplicacin de dibujo. Puede seleccionar un objeto ya dibujado en el
lienzo y pegar una instancia ms del mismo objeto. Desde la perspectiva del programador, no
podemos decidir qu objeto se copiar, ya que es una decisin de tiempo de
ejecucin. Necesitamos constructor de copia virtual para ayudar.
Del mismo modo, considere la aplicacin de tablero de clip. Una tabla de clip puede contener
diferentes tipos de objetos y copiar objetos de objetos existentes, pegarlos en el lienzo de
aplicacin. Una vez ms, qu tipo de objeto a copiar es una decisin de tiempo de ejecucin. El
constructor de copia virtual llena la brecha aqu. Vea el ejemplo a continuacin,
#include <iostream>
using namespace std;

//// LIBRARY SRART


class Base
{
public:
Base() { }

virtual // Ensures to invoke actual object destructor


~Base() { }

virtual void ChangeAttributes() = 0;

// The "Virtual Constructor"


static Base *Create(int id);

// The "Virtual Copy Constructor"


virtual Base *Clone() = 0;
};
class Derived1 : public Base
{
public:
Derived1()
{
cout << "Derived1 created" << endl;
}

Derived1(const Derived1& rhs)


{
cout << "Derived1 created by deep copy" << endl;
}

~Derived1()
{
cout << "~Derived1 destroyed" << endl;
}

void ChangeAttributes()
{
cout << "Derived1 Attributes Changed" << endl;
}

Base *Clone()
{
return new Derived1(*this);
}
};

class Derived2 : public Base


{
public:
Derived2()
{
cout << "Derived2 created" << endl;
}

Derived2(const Derived2& rhs)


{
cout << "Derived2 created by deep copy" << endl;
}

~Derived2()
{
cout << "~Derived2 destroyed" << endl;
}

void ChangeAttributes()
{
cout << "Derived2 Attributes Changed" << endl;
}

Base *Clone()
{
return new Derived2(*this);
}
};

class Derived3 : public Base


{
public:
Derived3()
{
cout << "Derived3 created" << endl;
}

Derived3(const Derived3& rhs)


{
cout << "Derived3 created by deep copy" << endl;
}

~Derived3()
{
cout << "~Derived3 destroyed" << endl;
}

void ChangeAttributes()
{
cout << "Derived3 Attributes Changed" << endl;
}

Base *Clone()
{
return new Derived3(*this);
}
};

// We can also declare "Create" outside Base.


// But is more relevant to limit it's scope to Base
Base *Base::Create(int id)
{
// Just expand the if-else ladder, if new Derived class is created
// User need not be recompiled to create newly added class objects

if( id == 1 )
{
return new Derived1;
}
else if( id == 2 )
{
return new Derived2;
}
else
{
return new Derived3;
}
}
//// LIBRARY END

//// UTILITY SRART


class User
{
public:
User() : pBase(0)
{
// Creates any object of Base heirarchey at runtime

int input;
cout << "Enter ID (1, 2 or 3): ";
cin >> input;

while( (input != 1) && (input != 2) && (input != 3) )


{
cout << "Enter ID (1, 2 or 3 only): ";
cin >> input;
}

// Create objects via the "Virtual Constructor"


pBase = Base::Create(input);
}

~User()
{
if( pBase )
{
delete pBase;
pBase = 0;
}
}

void Action()
{
// Duplicate current object
Base *pNewBase = pBase->Clone();

// Change its attributes


pNewBase->ChangeAttributes();

// Dispose the created object


delete pNewBase;
}

private:
Base *pBase;
};

//// UTILITY END

//// Consumer of User (UTILITY) class


int main()
{
User *user = new User();

user->Action();

delete user;
}

Clase de usuario que crea un objeto con la ayuda del constructor virtual. El objeto a crear se
basa en la entrada del usuario. Action () hace que el duplicado del objeto que se est creando y
modifique sus atributos. El objeto duplicado que se crea con la ayuda de la funcin
virtual Clone () que tambin se considera como constructor de copia virtual. El concepto detrs
del mtodo Clone () es el bloque de construccin del patrn de prototipo.
C ++ Internals | Constructores por defecto | Serie 1
Detallado ateriormente

Cundo se destruyen los objetos estticos?


Tenga cuidado de estas dos personas
Nuevos amigos y viejos enemigos - Kabir
Qu es la palabra clave esttica en C ++?
Static keyword se puede aplicar a variables locales, funciones, miembros de datos de clase y
objetos en C ++.La variable local esttica conserva sus valores entre la llamada de la funcin e
inicializada solamente una vez.La funcin esttica se puede llamar directamente usando el
operador de la resolucin del alcance precedido por el nombre de la clase (vase esto , esto y
esto para ms detalles). C ++ tambin admite objetos estticos.
Qu son los objetos estticos en C ++?
Un objeto se vuelve esttico cuando se utiliza una palabra clave esttica en su
declaracin. Consulte las dos instrucciones siguientes, por ejemplo, en C ++.
Prueba t; // Objeto basado en la pila
Prueba esttica t1; // Objeto esttico

Primera sentencia cuando se ejecuta crea un objeto en la pila significa que el almacenamiento
se asigna en la pila. Los objetos basados en pila tambin se llaman objetos automticos u
objetos locales. El objeto esttico se inicializan slo una vez y viven hasta que termina el
programa. El objeto local se crea cada vez que se encuentra su declaracin en la ejecucin del
programa.
Los objetos estticos se asignan el almacenamiento en el rea de almacenamiento esttico. El
objeto esttico se destruye al finalizar el programa. C ++ admite objetos estticos locales y
objetos estticos globales
A continuacin se muestra un ejemplo que muestra el uso de objetos estticos locales.
#include <iostream>
class Test
{
public:
Test()
{
std::cout << "Constructor is executed\n";
}
~Test()
{
std::cout << "Destructor is executed\n";
}
};
void myfunc()
{
static Test obj;
} // Object obj is still not destroyed because it is static

int main()
{
std::cout << "main() starts\n";
myfunc(); // Destructor will not be called here
std::cout << "main() terminates\n";
return 0;
}
Salida:
Principal comienza
El constructor se ejecuta
Principal () termina
Destructor se ejecuta

Si observamos la salida de este programa de cerca, podemos ver que el destructor para el
objeto local llamado obj no se llama despus de su constructor se ejecuta porque el objeto local
es esttico por lo que tiene alcance hasta la vida del programa por lo que es destructor ser
Cuando se termina main ().
Qu sucede cuando eliminamos la esttica en el programa anterior?
Como un experimento si eliminamos la palabra clave esttica de la funcin global myfunc (),
obtendremos la salida como a continuacin:
Principal comienza
El constructor se llama
Destructor se llama
Principal () termina

Esto se debe a que el objeto ahora es un objeto basado en la pila y se destruye cuando se sale
del mbito y su destructor se llamar.
Qu hay de objetos estticos globales?
El siguiente programa demuestra el uso del objeto esttico global.
#include <iostream>
class Test
{
public:
int a;
Test()
{
a = 10;
std::cout << "Constructor is executed\n";
}
~Test()
{
std::cout << "Destructor is executed\n";
}
};
static Test obj;
int main()
{
std::cout << "main() starts\n";
std::cout << obj.a;
std::cout << "\nmain() terminates\n";
return 0;
}

Salida:
El constructor se ejecuta
Principal comienza
10
Principal () termina
Destructor se ejecuta
Es posible llamar constructor y destructor
explcitamente?
Todos los das de los afligidos son malos, pero l
Que es de un corazn alegre tiene una fiesta continua.
Proverbios 15:15 (Biblia)

Constructor es una funcin de miembro especial que es automticamente llamada por el


compilador cuando se crea el objeto y destructor tambin es una funcin de miembro especial
que tambin es implcitamente llamada por el compilador cuando el objeto sale del mbito de
aplicacin. Tambin se les llama cuando el objeto asignado dinmicamente se asigna y
destruye, el nuevo operador asigna el almacenamiento y llama al constructor, elimina el
destructor de las llamadas del operador y libera la memoria asignada por new.
Es posible llamar constructor y destructor explcitamente?
S, es posible llamar explcitamente a las funciones de miembros especiales por el
programador. El programa siguiente llama constructor y destructor explcitamente.
#include <iostream>
using namespace std;

class Test
{
public:
Test() { cout << "Constructor is executed\n"; }
~Test() { cout << "Destructor is executed\n"; }
};

int main()
{
Test(); // Explicit call to constructor
Test t; // local object
t.~Test(); // Explicit call to destructor
return 0;
}

Salida:
El constructor se ejecuta
Destructor se ejecuta
El constructor se ejecuta
Destructor se ejecuta
Destructor se ejecuta

Cuando el constructor se llama explcitamente el compilador crea un objeto temporal sin


nombre y se destruye inmediatamente. Es por eso que la segunda lnea en la salida es llamada
al destructor.
Aqu hay una conversacin entre m y el Dr. Bjarne Stroustrup va correo electrnico sobre este
tema:
Me: Por qu C ++ permite llamar al constructor explcitamente? No crees que esto no
debera ser?
Dr. Bjarne: No. Los objetos temporales de los tipos de clases son tiles.
La seccin 12.4 / 14 de la norma C ++ dice que
Una vez que se invoca un destructor para un objeto, el objeto ya no existe; El comportamiento
es indefinido si el destructor es invocado para un objeto cuya vida til ha terminado [Ejemplo: si
el destructor de un objeto automtico es invocado explcitamente, y el bloque se deja
posteriormente de una manera que normalmente invocara la destruccin implcita del objeto,
Comportamiento no est definido. -end ejemplo].
Como se mencion aqu , nunca deberamos llamar destructor explcitamente en objetos
locales (automticos), porque realmente se pueden obtener resultados malos haciendo eso.
Los objetos locales son automticamente destruidos por el compilador cuando salen del
alcance y esto es la garanta del lenguaje C ++. En general, las funciones de miembros
especiales no deben ser llamadas explcitamente.
Constructor y destructor tambin se puede llamar desde la funcin miembro de clase. Vea el
siguiente programa:
#include <iostream>
using namespace std;

class Test
{
public:
Test() { cout << "Constructor is executed\n"; }
~Test() { cout << "Destructor is executed\n"; }
void show() { Test(); this->Test::~Test(); }
};

int main()
{
Test t;
t.show();
return 0;
}

Salida:
El constructor se ejecuta
El constructor se ejecuta
Destructor se ejecuta
Destructor se ejecuta
Destructor se ejecuta

La llamada explcita al destructor slo es necesaria cuando el objeto se coloca en una


ubicacin particular en la memoria mediante el uso de la colocacin nueva. Destructor no se
debe llamar explcitamente cuando el objeto se asigna dinmicamente porque el operador de
eliminacin llama automticamente al destructor.
Como un ejercicio predecir la salida del siguiente programa:
#include <iostream>
using namespace std;

class Test
{
public:
Test() { cout << "Constructor is executed\n"; }
~Test() { cout << "Destructor is executed\n"; }
friend void fun(Test t);
};
void fun(Test t)
{
Test();
t.~Test();
}
int main()
{
Test();
Test t;
fun(t);
return 0;
}
Herencia

Qu todo se hereda de la clase de padre en C ++?


Las siguientes son las cosas que una clase derivada hereda de su padre.
1) Cada miembro de datos definido en la clase padre (aunque estos miembros no siempre
pueden ser
Accesible en la clase derivada!)
2) Cada funcin de miembro ordinario de la clase de padre (aunque tales miembros no siempre
pueden ser
Accesible en la clase derivada!)
3) El mismo diseo de datos inicial que la clase base.
Las siguientes son las cosas que una clase derivada no hereda de su padre:
1) Los constructores y destructor de la clase base.
2) Los amigos de la clase base

Funciones Virtuales y Polimorfismo de Tiempo de


Ejecucin en C ++ | Conjunto 1 (Introduccin)
Considere el siguiente programa simple que es un ejemplo de polimorfismo de tiempo de
ejecucin.
Lo ms importante a tener en cuenta sobre el programa es que la funcin de clase derivada se
llama utilizando un puntero de clase base. La idea es que las funciones virtuales se llaman
segn el tipo de objeto apuntado o referido, no segn el tipo de puntero o referencia. En otras
palabras, las funciones virtuales se resuelven tarde, en tiempo de ejecucin.
#include<iostream>
using namespace std;

class Base
{
public:
virtual void show() { cout<<" In Base \n"; }
};

class Derived: public Base


{
public:
void show() { cout<<"In Derived \n"; }
};

int main(void)
{
Base *bp = new Derived;
bp->show(); // RUN-TIME POLYMORPHISM
return 0;
}

Salida:
En derivado

Cul es el uso?
Las funciones virtuales nos permiten crear una lista de punteros de clase base y mtodos de
llamada de cualquiera de las clases derivadas sin siquiera saber el tipo de objeto de clase
derivado. Por ejemplo, considere un software de gestin de empleados para una organizacin,
deje que el cdigo tiene una clase base simpleEmpleado , la clase contiene funciones virtuales
como raiseSalary () , transfer () , promote () , etc. Diferentes tipos de empleados
como Manager , Engineer , ..etc pueden tener sus propias implementaciones de las funciones
virtuales presentes en la clase base Employee . En nuestro software completo, slo
necesitamos pasar una lista de empleados por todas partes y llamar a funciones adecuadas sin
siquiera saber el tipo de empleado. Por ejemplo, podemos aumentar fcilmente el salario de
todos los empleados iterando a travs de la lista de empleados. Cada tipo de empleado puede
tener su propia lgica en su clase, no tenemos quepreocuparse porque si raiseSalary () est
presente para un tipo de empleado especfico , slo esa funcin sera llamada.
class Employee
{
public:
virtual void raiseSalary()
{ /* common raise salary code */ }

virtual void promote()


{ /* common promote code */ }
};

class Manager: public Employee {


virtual void raiseSalary()
{ /* Manager specific raise salary code, may contain
increment of manager specific incentives*/ }

virtual void promote()


{ /* Manager specific promote */ }
};

// Similarly, there may be other types of employees

// We need a very simple function to increment salary of all employees


// Note that emp[] is an array of pointers and actual pointed objects can
// be any type of employees. This function should ideally be in a class
// like Organization, we have made it global to keep things simple
void globalRaiseSalary(Employee *emp[], int n)
{
for (int i = 0; i < n; i++)
emp[i]->raiseSalary(); // Polymorphic Call: Calls raiseSalary()
// according to the actual object, not
// according to the type of
pointer
}

Como globalRaiseSalary (), puede haber muchas otras operaciones que se pueden hacer
correctamente en una lista de empleados sin siquiera saber el tipo de objeto real.
Las funciones virtuales son tan tiles que lenguajes posteriores como Java mantienen todos
los mtodos comovirtuales por defecto .
Cmo hace el compilador esta magia de resolucin tarda?
Compiler mantiene dos cosas a esta magia:
Vtable: Una tabla de punteros de funciones. Se mantiene por clase.
Vptr: Un puntero a vtable. Se mantiene por objeto (vea estocomo ejemplo).
El compilador agrega cdigo adicional en dos lugares para mantener y usar vptr.
1) Cdigo en cada constructor.Este cdigo establece vptr del objeto que se est creando. Este
cdigo establece vptr para apuntar a vtable de la clase.
2) Cdigo con llamada de funcin polimrfica (por ejemplo, bp-> show () en el cdigo
anterior). Dondequiera que se haga una llamada polimrfica, el compilador inserta cdigo para
buscar primero vptr usando puntero de clase base o referencia (en el ejemplo anterior, ya que
el objeto apuntado o referido es de tipo derivado, se accede a vptr de clase derivada). Una vez
que se obtiene vptr, se puede acceder a vtable de la clase derivada. Usandovtable, se accede
y se llama a la direccin de la funcin de clase derivada derivada show ().
Es esta una forma estndar para la implementacin de tiempo de ejecucin
polimorfismo en C + +?
Los estndares de C ++ no exigen exactamente cmo se debe implementar el polimorfismo de
tiempo de ejecucin, pero los compiladores generalmente utilizan variaciones menores en el
mismo modelo bsico.

Herencia mltiple en C ++
Multiple Herencia es una caracterstica de C ++ donde una clase puede heredar de ms de una
clase.
Los constructores de las clases heredadas se llaman en el mismo orden en que se
heredan. Por ejemplo, en el siguiente programa, el constructor de B se llama antes del
constructor de A.
#include<iostream>
using namespace std;

class A
{
public:
A() { cout << "A's constructor called" << endl; }
};

class B
{
public:
B() { cout << "B's constructor called" << endl; }
};

class C: public B, public A // Note the order


{
public:
C() { cout << "C's constructor called" << endl; }
};

int main()
{
C c;
return 0;
}

Salida:
El constructor de B llamado
El constructor de A llamado
El constructor de C llamado

Los destructores son llamados en orden inverso de los constructores.


El problema del diamante
El problema del diamante se produce cuando dos superclases de una clase tienen una clase
base comn. Por ejemplo, en el siguiente diagrama, la clase TA obtiene dos copias de todos
los atributos de la clase Person, esto causa ambigedades.

Por ejemplo, considere el siguiente programa.


#include<iostream>
using namespace std;
class Person {
// Data members of person
public:
Person(int x) { cout << "Person::Person(int ) called" << endl; }
};

class Faculty : public Person {


// data members of Faculty
public:
Faculty(int x):Person(x) {
cout<<"Faculty::Faculty(int ) called"<< endl;
}
};

class Student : public Person {


// data members of Student
public:
Student(int x):Person(x) {
cout<<"Student::Student(int ) called"<< endl;
}
};

class TA : public Faculty, public Student {


public:
TA(int x):Student(x), Faculty(x) {
cout<<"TA::TA(int ) called"<< endl;
}
};

int main() {
TA ta1(30);
}
Person :: Person (int) called
Facultad :: Facultad (int) llamada
Person :: Person (int) called
Estudiante :: Estudiante (int) llamado
TA :: TA (int) llamado

En el programa anterior, el constructor de 'Person' se llama dos veces. Destructor de 'Person'


tambin ser llamado dos veces cuando el objeto 'ta1' es destructed. As que el objeto 'ta1'
tiene dos copias de todos los miembros de 'Person', esto causa ambigedades. La solucin a
este problema es la palabra clave 'virtual' .Hacemos las clases 'Facultad' y 'Estudiante' como
clases de base virtual para evitar dos copias de 'Persona' en la clase 'TA'. Por ejemplo,
considere el siguiente programa.
#include<iostream>
using namespace std;
class Person {
public:
Person(int x) { cout << "Person::Person(int ) called" << endl; }
Person() { cout << "Person::Person() called" << endl; }
};

class Faculty : virtual public Person {


public:
Faculty(int x):Person(x) {
cout<<"Faculty::Faculty(int ) called"<< endl;
}
};

class Student : virtual public Person {


public:
Student(int x):Person(x) {
cout<<"Student::Student(int ) called"<< endl;
}
};

class TA : public Faculty, public Student {


public:
TA(int x):Student(x), Faculty(x) {
cout<<"TA::TA(int ) called"<< endl;
}
};

int main() {
TA ta1(30);
}
Salida:
Persona :: Person () llamada
Facultad :: Facultad (int) llamada
Estudiante :: Estudiante (int) llamado
TA :: TA (int) llamado

En el programa anterior, el constructor de 'Person' se llama una vez. Una cosa importante a
tener en cuenta en la salida anterior es, el constructor por defecto de 'Person' se
llama. Cuando usamos palabras clave 'virtuales', el constructor por defecto de la clase
grandparent se llama por defecto, incluso si las clases padre llaman explcitamente a
constructor parametrizado.
Cmo llamar al constructor parametrizado de la clase 'Person'? El constructor tiene que
ser llamado en la clase 'TA'. Por ejemplo, consulte el siguiente programa.
#include<iostream>
using namespace std;
class Person {
public:
Person(int x) { cout << "Person::Person(int ) called" << endl; }
Person() { cout << "Person::Person() called" << endl; }
};

class Faculty : virtual public Person {


public:
Faculty(int x):Person(x) {
cout<<"Faculty::Faculty(int ) called"<< endl;
}
};

class Student : virtual public Person {


public:
Student(int x):Person(x) {
cout<<"Student::Student(int ) called"<< endl;
}
};

class TA : public Faculty, public Student {


public:
TA(int x):Student(x), Faculty(x), Person(x) {
cout<<"TA::TA(int ) called"<< endl;
}
};

int main() {
TA ta1(30);
}

Salida:
Person :: Person (int) called
Facultad :: Facultad (int) llamada
Estudiante :: Estudiante (int) llamado
TA :: TA (int) llamado

En general, no se le permite llamar directamente al constructor del abuelo, sino que tiene que
ser llamado a travs de la clase padre. Slo se permite cuando se utiliza la palabra clave
'virtual'.
Como un ejercicio, predecir el resultado de los siguientes programas.
Pregunta 1
#include<iostream>
using namespace std;

class A
{
int x;
public:
void setX(int i) {x = i;}
void print() { cout << x; }
};

class B: public A
{
public:
B() { setX(10); }
};

class C: public A
{
public:
C() { setX(20); }
};

class D: public B, public C {


};

int main()
{
D d;
d.print();
return 0;
}

Pregunta 2
#include<iostream>
using namespace std;
class A
{
int x;
public:
A(int i) { x = i; }
void print() { cout << x; }
};

class B: virtual public A


{
public:
B():A(10) { }
};

class C: virtual public A


{
public:
C():A(10) { }
};

class D: public B, public C {


};
int main()
{
D d;
d.print();
return 0;
}

Qu sucede cuando se da acceso ms restrictivo


a un mtodo de clase derivado en C ++?
Hemos discutido un tema similar en Java aqu . A diferencia de Java, C ++ permite dar un
acceso ms restrictivo a los mtodos de clase derivados. Por ejemplo, el siguiente programa
compila bien.
#include<iostream>
using namespace std;

class Base {
public:
virtual int fun(int i) { }
};

class Derived: public Base {


private:
int fun(int x) { }
};

int main()
{ }

En el programa anterior, si cambiamos main () a following, obtendremos error de compilador


porque fun () es privado en la clase derivada.
int main()
{
Derived d;
d.fun(1);
return 0;
}

Qu pasa con el siguiente programa?


#include<iostream>
using namespace std;
class Base {
public:
virtual int fun(int i) { cout << "Base::fun(int i) called"; }
};

class Derived: public Base {


private:
int fun(int x) { cout << "Derived::fun(int x) called"; }
};
int main()
{
Base *ptr = new Derived;
ptr->fun(10);
return 0;
}
Salida:
Derived :: fun (int x) llamado

En el programa anterior, la funcin privada "Derived :: fun (int)" se llama a travs de un puntero
de clase base, el programa funciona bien porque fun () es pblico en la clase base. Los
especificadores de acceso se comprueban en tiempo de compilacin y fun () es pblico en la
clase base. En tiempo de ejecucin, slo se llama a la funcin correspondiente al objeto
sealado y no se comprueba el especificador de acceso. As que una funcin privada de clase
derivada se llama a travs de un puntero de clase base.

Slicing de objetos en C ++
En C ++, un objeto de clase derivado se puede asignar a un objeto de clase base, pero la otra
forma no es posible.
class Base { int x, y; };

class Derived : public Base { int z, w; };

int main()
{
Derived d;
Base b = d; // Object Slicing, z and w of d are sliced off
}

El corte de objetos ocurre cuando un objeto de clase derivado se asigna a un objeto de clase
base, los atributos adicionales de un objeto de clase derivado se cortan para formar el objeto
de clase base.
#include <iostream>
using namespace std;

class Base
{
protected:
int i;
public:
Base(int a) { i = a; }
virtual void display()
{ cout << "I am Base class object, i = " << i << endl; }
};

class Derived : public Base


{
int j;
public:
Derived(int a, int b) : Base(a) { j = b; }
virtual void display()
{ cout << "I am Derived class object, i = "
<< i << ", j = " << j << endl; }
};

// Global method, Base class object is passed by value


void somefunc (Base obj)
{
obj.display();
}

int main()
{
Base b(33);
Derived d(45, 54);
somefunc(b);
somefunc(d); // Object Slicing, the member j of d is sliced off
return 0;
}

Salida:
Soy objeto de clase base, i = 33
Soy objeto de clase base, i = 45

Podemos evitar el comportamiento inesperado anterior con el uso de punteros o referencias. El


corte de objetos no se produce cuando se pasan punteros o referencias a objetos como
argumentos de funcin, ya que un puntero o referencia de cualquier tipo toma la misma
cantidad de memoria. Por ejemplo, si cambiamos el mtodo global myfunc () en el programa
anterior al siguiente, el corte de objetos no sucede.
// rest of code is similar to above
void somefunc (Base &obj)
{
obj.display();
}
// rest of code is similar to above

Salida:
Soy objeto de clase base, i = 33
Soy objeto de clase Derivado, i = 45, j = 54

Obtenemos la misma salida si usamos punteros y cambiamos el programa a siguiente.


// rest of code is similar to above
void somefunc (Base *objp)
{
objp->display();
}

int main()
{
Base *bp = new Base(33) ;
Derived *dp = new Derived(45, 54);
somefunc(bp);
somefunc(dp); // No Object Slicing
return 0;
}

Salida:
Soy objeto de clase base, i = 33
Soy objeto de clase Derivado, i = 45, j = 54

El corte de objetos puede evitarse haciendo que la funcin de clase base sea puramente virtual
all al no permitir la creacin de objetos. No es posible crear el objeto de una clase que
contiene un mtodo virtual puro.
Ocultar todos los mtodos sobrecargados con el
mismo nombre en la clase base
En C ++, si una clase derivada redefine el mtodo de miembro de clase base, entonces todos
los mtodos de clase base con el mismo nombre se ocultan en la clase derivada.
Por ejemplo, el programa siguiente no compila. En el siguiente programa, Derived redefine el
mtodo de la base fun () y esto hace que fun (int i) se oculte.
#include<iostream>

using namespace std;

class Base
{
public:
int fun()
{
cout<<"Base::fun() called";
}
int fun(int i)
{
cout<<"Base::fun(int i) called";
}
};

class Derived: public Base


{
public:
int fun()
{
cout<<"Derived::fun() called";
}
};

int main()
{
Derived d;
d.fun(5); // Compiler Error
return 0;
}

Incluso si la firma del mtodo de clase derivada es diferente, todos los mtodos sobrecargados
en la clase base se ocultan. Por ejemplo, en el siguiente programa, Derived :: fun (char) hace
que Base :: fun () y Base :: fun (int) estn ocultos.
#include<iostream>
using namespace std;

class Base
{
public:
int fun()
{
cout<<"Base::fun() called";
}
int fun(int i)
{
cout<<"Base::fun(int i) called";
}
};
class Derived: public Base
{
public:
int fun(char c) // Makes Base::fun() and Base::fun(int ) hidden
{
cout<<"Derived::fun(char c) called";
}
};

int main()
{
Derived d;
d.fun(); // Compiler Error
return 0;
}

Obsrvese que los hechos anteriores son verdaderos tanto para los mtodos estticos como
para los no estticos.

Amigos(Friends)

Clase de amigo y funcin en C ++


Clase de amigo Una clase de amigo puede acceder a miembros privados y protegidos de otra
clase en la que se declara como amigo. A veces es til permitir que una clase particular acceda
a miembros privados de otra clase.Por ejemplo, una clase LinkedList puede tener acceso a
miembros privados de Node.
class Node
{
private:
int key;
Node *next;
/* Other members of Node Class */

friend class LinkedList; // Now class LinkedList can


// access private members of Node
};

Funcin de Amigo Como clase de amigo, una funcin de amigo puede recibir una donacin
especial para acceder a miembros privados y protegidos. Una funcin de amigo puede ser:
A) Un mtodo de otra clase
B) Una funcin global

class Node
{
private:
int key;
Node *next;

/* Other members of Node Class */


friend int LinkedList::search(); // Only search() of linkedList
// can access internal members
};
Los siguientes son algunos puntos importantes sobre las funciones y clases de amigos:
1) Los amigos deben ser usados solamente para el propsito limitado. Demasiadas funciones
o clases externas se declaran como amigos de una clase con datos protegidos o privados,
disminuye el valor de la encapsulacin de clases separadas en la programacin orientada a
objetos.
2) La amistad no es mutua. Si una clase A es amiga de B, entonces B no se convierte
automticamente en amiga de A.
3) La amistad no es heredada (Vea esto para ms detalles)
4) El concepto de amigos no est en Java.

Un programa simple y completo de C ++ para demostrar la clase del amigo


#include <iostream>
class A {
private:
int a;
public:
A() { a=0; }
friend class B; // Friend Class
};

class B {
private:
int b;
public:
void showA(A& x) {
// Since B is friend of A, it can access
// private members of A
std::cout << "A::a=" << x.a;
}
};

int main() {
A a;
B b;
b.showA(a);
return 0;
}

Salida:
A :: a = 0

Un programa simple y completo de C ++ para demostrar la funcin de amigo de otra


clase
#include <iostream>

class B;

class A
{
public:
void showB(B& );
};

class B
{
private:
int b;
public:
B() { b = 0; }
friend void A::showB(B& x); // Friend function
};

void A::showB(B &x)


{
// Since show() is friend of B, it can
// access private members of B
std::cout << "B::b = " << x.b;
}

int main()
{
A a;
B x;
a.showB(x);
return 0;
}

Salida:
B :: b = 0

Un simple y completo programa C ++ para demostrar a un amigo global


#include <iostream>

class A
{
int a;
public:
A() {a = 0;}
friend void showA(A&); // global friend function
};

void showA(A& x) {
// Since showA() is a friend, it can access
// private members of A
std::cout << "A::a=" << x.a;
}

int main()
{
A a;
showA(a);
return 0;
}

Salida:
A :: a = 0

Herencia y amistad
En C ++, la amistad no se hereda. Si una clase base tiene una funcin de amigo, entonces la
funcin no se convierte en un amigo de la clase derivada (es).
Por ejemplo, el siguiente programa imprime un error porque show () que es un amigo de la
clase base A intenta acceder a datos privados de la clase derivada B.
#include <iostream>
using namespace std;

class A
{
protected:
int x;
public:
A() { x = 0;}
friend void show();
};

class B: public A
{
public:
B() : y (0) {}
private:
int y;
};

void show()
{
B b;
cout << "The default value of A::x = " << b.x;

// Can't access private member declared in class 'B'


cout << "The default value of B::y = " << b.y;
}

int main()
{
show();
getchar();
return 0;
}

Funciones virtuales

Funciones Virtuales y Polimorfismo de Tiempo de


Ejecucin en C ++ | Conjunto 1 (Introduccin)
Detallado en Herencia

Argumentos predeterminados y funcin virtual


Predecir la salida del siguiente programa de C ++.
#include <iostream>
using namespace std;
class Base
{
public:
virtual void fun ( int x = 0 )
{
cout << "Base::fun(), x = " << x << endl;
}
};

class Derived : public Base


{
public:
virtual void fun ( int x )
{
cout << "Derived::fun(), x = " << x << endl;
}
};

int main()
{
Derived d1;
Base *bp = &d1;
bp->fun();
return 0;
}

Salida:
Derivado :: fun (), x = 0

Si observamos ms de cerca la salida, observamos que se llama fun () de la clase derivada y


se utiliza el valor por defecto de la clase base fun ().
Los argumentos por defecto no participan en la firma de funciones. As que las firmas de fun ()
en la clase base y la clase derivada se consideran iguales, por lo tanto, la diversin () de la
clase base se sobreescribe. Adems, el valor predeterminado se utiliza en tiempo de
compilacin. Cuando el compilador ve que falta un argumento en una llamada de funcin,
sustituye el valor predeterminado dado. Por lo tanto, en el programa anterior, el valor de x se
sustituye en tiempo de compilacin, y en tiempo de ejecucin derivado de la clase fun () se
llama.
Ahora predecir la salida del siguiente programa.
#include <iostream>
using namespace std;

class Base
{
public:
virtual void fun ( int x = 0)
{
cout << "Base::fun(), x = " << x << endl;
}
};

class Derived : public Base


{
public:
virtual void fun ( int x = 10 ) // NOTE THIS CHANGE
{
cout << "Derived::fun(), x = " << x << endl;
}
};

int main()
{
Derived d1;
Base *bp = &d1;
bp->fun();
return 0;
}

La salida de este programa es igual que el programa anterior. La razn es la misma, el valor
predeterminado se sustituye en tiempo de compilacin. La funcin fun () se llama en bp, que es
un puntero de tipo Base. Por lo tanto, el compilador sustituye 0 (no 10).
En general, es una buena prctica evitar los valores predeterminados en las funciones virtuales
para evitar confusiones (consulte esto para ms detalles)

Funciones virtuales en clases derivadas


En C ++, una vez que una funcin miembro se declara como una funcin virtual en una clase
base, se convierte en virtual en cada clase derivada de esa clase base. En otras palabras, no
es necesario utilizar la palabra clave virtual en la clase derivada mientras se declaran versiones
redefinidas de la funcin de clase base virtual.
Fuente: http://www.umsl.edu/~subramaniana/virtual2.html
Por ejemplo, el siguiente programa imprime "C :: fun () llamado" as B :: fun () se convierte en
virtual automticamente.
#include<iostream>

using namespace std;

class A {
public:
virtual void fun()
{ cout<<"\n A::fun() called ";}
};

class B: public A {
public:
void fun()
{ cout<<"\n B::fun() called "; }
};

class C: public B {
public:
void fun()
{ cout<<"\n C::fun() called "; }
};

int main()
{
C c; // An object of class C
B *b = &c; // A pointer of type B* pointing to c
b->fun(); // this line prints "C::fun() called"
getchar();
return 0;
}
Pueden las funciones estticas ser virtuales en C
++?
En C ++, una funcin de miembro esttico de una clase no puede ser virtual . Por ejemplo,
debajo programa da error de compilacin.
#include<iostream>

using namespace std;

class Test
{
public:
// Error: Virtual member functions cannot be static
virtual static void fun() { }
};

Adems, la funcin de miembro esttico no puede ser constante y voltil . El cdigo


siguiente tambin falla en la compilacin.
#include<iostream>

using namespace std;

class Test
{
public:
// Error: Static member function cannot be const
static void fun() const { }
};

Destructor virtual
La eliminacin de un objeto de clase derivado mediante un puntero a una clase base que tiene
un destructor no virtual da lugar a un comportamiento no definido. Para corregir esta situacin,
la clase base debe ser definida con un destructor virtual.

Por ejemplo, el siguiente programa da como resultado un comportamiento no definido. Aunque


la salida del siguiente programa puede ser diferente en compiladores diferentes, cuando se
compila con Dev-CPP, se imprime a continuacin.
Construyendo base
Construyendo derivados
Base de destruccin
// A program without virtual destructor causing undefined behavior
#include<iostream>

using namespace std;

class base {
public:
base()
{ cout<<"Constructing base \n"; }
~base()
{ cout<<"Destructing base \n"; }
};
class derived: public base {
public:
derived()
{ cout<<"Constructing derived \n"; }
~derived()
{ cout<<"Destructing derived \n"; }
};

int main(void)
{
derived *d = new derived();
base *b = d;
delete b;
getchar();
return 0;
}

Haciendo que la clase base destructor virtual garantice que el objeto de la clase derivada es
destructed correctamente, es decir, se llaman a la clase base ya los destructores de clase
derivados. Por ejemplo, despus de imprimir el programa:
Construyendo base
Construyendo derivados
Destructing derivado
Base de destruccin
// A program with virtual destructor
#include<iostream>

using namespace std;

class base {
public:
base()
{ cout<<"Constructing base \n"; }
virtual ~base()
{ cout<<"Destructing base \n"; }
};

class derived: public base {


public:
derived()
{ cout<<"Constructing derived \n"; }
~derived()
{ cout<<"Destructing derived \n"; }
};

int main(void)
{
derived *d = new derived();
base *b = d;
delete b;
getchar();
return 0;
}

Como una pauta, cada vez que usted tiene una funcin virtual en una clase, usted debe
agregar inmediatamente un destructor virtual (incluso si no hace nada). De esta manera, te
asegura contra cualquier sorpresa ms tarde.
Constructor Virtual
Constructor de copia virtual
Detalllados en Constructores y Destructores

RTTI (informacin de tipo de tiempo de


ejecucin) en C ++
En C ++, RTTI (informacin de tipo de tiempo de ejecucin) est disponible slo para las clases
que tienen al menos una funcin virtual.
Por ejemplo, dynamic_cast utiliza RTTI y el siguiente programa falla con error " no puede
dynamic_cast` b '(de tipo `clase B *') escribir` class D * '(el tipo de fuente no es
polimrfico) "porque no hay funcin virtual en el Clase base
#include<iostream>

using namespace std;

class B { };
class D: public B {};

int main()
{
B *b = new D;
D *d = dynamic_cast<D*>(b);
if(d != NULL)
cout<<"works";
else
cout<<"cannot cast B* to D*";
getchar();
return 0;
}

Agregar una funcin virtual a la clase base B hace que funcione.


#include<iostream>
using namespace std;

class B { virtual void fun() {} };


class D: public B { };

int main()
{
B *b = new D;
D *d = dynamic_cast<D*>(b);
if(d != NULL)
cout<<"works";
else
cout<<"cannot cast B* to D*";
getchar();
return 0;
}

Pueden las funciones virtuales ser privadas en C


++?
En C ++, las funciones virtuales pueden ser privadas y pueden ser anuladas por la clase
derivada. Por ejemplo, el programa siguiente compila y ejecuta bien.
#include<iostream>
using namespace std;

class Derived;

class Base {
private:
virtual void fun() { cout << "Base Fun"; }
friend int main();
};

class Derived: public Base {


public:
void fun() { cout << "Derived Fun"; }
};

int main()
{
Base *ptr = new Derived;
ptr->fun();
return 0;
}

Salida:
Diversin derivada ()

Hay pocas cosas a tener en cuenta en el programa anterior.


1) ptr es un puntero de tipo Base y apunta a un objeto de clase Derived . Cuando ptr-> fun
() es llamado, fun ()de Derived se ejecuta.
2) int main () es un amigo de Base. Si eliminamos esta amistad, el programa no compilar
(Ver esto ).Conseguimos error del compilador .
Tenga en cuenta que este comportamiento es totalmente diferente en Java. En Java, los
mtodos privados son finales por defecto y no se pueden sobreescribir.

Pueden las funciones virtuales estar en lnea?


Funciones virtuales se utilizan para lograr el tiempo de ejecucin polimorfismo o decir
vinculacin tarda o vinculacin dinmica. Funciones en lnea se utilizan para la eficiencia. La
idea detrs de las funciones en lnea es que siempre que se llama a la funcin en lnea cdigo
de la funcin en lnea se inserta o sustituye en el punto de la funcin en lnea llamada en
tiempo de compilacin. Las funciones en lnea son muy tiles cuando se utilizan
frecuentemente pequeas funciones y se las llama en un programa muchas veces.
De forma predeterminada, todas las funciones definidas dentro de la clase se consideran
implcita o automticamente como inline, excepto las funciones virtuales (Obsrvese que inline
es una peticin al compilador y su eleccin de compiladores para hacer inlining o no).
Siempre que se llama a la funcin virtual usando referencia de clase base o puntero no se
puede inline (porque la llamada se resuelve en tiempo de ejecucin), pero cuando se llama
usando el objeto (sin referencia o puntero) de esa clase, se puede inline porque el compilador
conoce la clase exacta de El objeto en tiempo de compilacin.

#include <iostream>
using namespace std;
class Base
{
public:
virtual void who()
{
cout << "I am Base\n";
}
};
class Derived: public Base
{
public:
void who()
{
cout << "I am Derived\n";
}
};

int main()
{
// note here virtual function who() is called through
// object of the class (it will be resolved at compile
// time) so it can be inlined.
Base b;
b.who();

// Here virtual function is called through pointer,


// so it cannot be inlined
Base *ptr = new Derived();
ptr->who();

return 0;
}

Funciones virtuales puras y clases abstractas en C


++
A veces la implementacin de toda la funcin no se puede proporcionar en una clase base
porque no conocemos la implementacin. Tal clase se llama clase abstracta. Por ejemplo, deje
que Shape sea una clase base. No podemos proporcionar la implementacin de la funcin
draw () en Shape, pero sabemos que cada clase derivada debe tener la implementacin de
draw (). Del mismo modo una clase Animal no tiene implementacin de move () (suponiendo
que todos los animales se mueven), pero todos los animales deben saber moverse. No
podemos crear objetos de clases abstractas.
Una funcin virtual pura (o funcin abstracta) en C ++ es una funcin virtual para la cual no
tenemos implementacin, solo la declaramos. Una funcin virtual pura se declara asignando 0
en la declaracin. Vea el siguiente ejemplo.
// An abstract class
class Test
{
// Data members of class
public:
// Pure Virtual Function
virtual void show() = 0;

/* Other members */
};
Un ejemplo completo:
Una funcin virtual pura es implementada por las clases que se derivan de una clase
abstracta. A continuacin se muestra un ejemplo sencillo para demostrar lo mismo.
#include<iostream>
using namespace std;

class Base
{
int x;
public:
virtual void fun() = 0;
int getX() { return x; }
};

// This class ingerits from Base and implements fun()


class Derived: public Base
{
int y;
public:
void fun() { cout << "fun() called"; }
};

int main(void)
{
Derived d;
d.fun();
return 0;
}

Salida:
Divertido () llamado
Algunos hechos interesantes:
1) Una clase es abstracta si tiene al menos una funcin virtual pura.
En el ejemplo siguiente, Test es una clase abstracta porque tiene una funcin virtual pura show
().
// pure virtual functions make a class abstract
#include<iostream>
using namespace std;

class Test
{
int x;
public:
virtual void show() = 0;
int getX() { return x; }
};

int main(void)
{
Test t;
return 0;
}

Salida:
Error de compilador: no se puede declarar variable 't' para ser de resumen
Tipo 'Prueba' porque las siguientes funciones virtuales son puras
Dentro de 'Test': note: virtual void Test :: show ()
2) Podemos tener punteros y referencias de tipo de clase abstracta.
Por ejemplo, el siguiente programa funciona bien.
#include<iostream>
using namespace std;

class Base
{
public:
virtual void show() = 0;
};

class Derived: public Base


{
public:
void show() { cout << "In Derived \n"; }
};

int main(void)
{
Base *bp = new Derived();
bp->show();
return 0;
}

Salida:
En derivado
3) Si no anulamos la funcin virtual pura en la clase derivada, la clase derivada tambin se
convierte en clase abstracta.
El ejemplo siguiente demuestra lo mismo.
#include<iostream>
using namespace std;
class Base
{
public:
virtual void show() = 0;
};

class Derived : public Base { };

int main(void)
{
Derived d;
return 0;
}

Error de compilador: no se puede declarar variable 'd' para ser de tipo abstracto
'Derivado' porque las siguientes funciones virtuales son puras dentro
'Derivado': virtual void Base :: show ()
4) Una clase abstracta puede tener constructores.
Por ejemplo, el programa siguiente compila y ejecuta bien.
#include<iostream>
using namespace std;

// An abstract class with constructor


class Base
{
protected:
int x;
public:
virtual void fun() = 0;
Base(int i) { x = i; }
};

class Derived: public Base


{
int y;
public:
Derived(int i, int j):Base(i) { y = j; }
void fun() { cout << "x = " << x << ", y = " << y; }
};

int main(void)
{
Derived d(4, 5);
d.fun();
return 0;
}

Salida:
X = 4, y = 5
Comparacin con Java
En Java, una clase puede hacerse abstracta mediante palabras clave abstractas. De manera
similar, una funcin puede hacerse pura virtual o abstracta utilizando una palabra clave
abstracta. Ver
Clases abstractas en Java para ms detalles.
Interfaz vs clases abstractas:
Una interfaz no tiene implementacin de ninguno de sus mtodos, puede considerarse como
una coleccin de declaraciones de mtodo. En C ++, una interfaz puede ser simulada haciendo
que todos los mtodos sean puramente virtuales. En Java, hay una palabra clave separada
para la interfaz.

Destructor virtual puro en C ++


No te preocupes por nada, pero en todo,
Con oracin y peticin, con accin de gracias, presente
Sus peticiones a Dios.
Filipenses 4: 6 (Biblia)

Puede un destructor ser virtual puro en C ++?


S, es posible tener destructor virtual puro. El destructor virtual puro es legal en C ++ estndar y
uno de lo ms importante es que si la clase contiene destructor virtual puro es necesario
proporcionar un cuerpo de funcin para el destructor virtual puro. Esto parece extrao que
cmo una funcin virtual es pura si se requiere un cuerpo de funcin? Pero los destructores
siempre se llaman en el orden inverso de la derivacin de clase. Esto significa que el destructor
de clase derivado se invocar primero y luego se llamar destructor de clase base. Si no se
proporciona la definicin para el destructor virtual puro, qu cuerpo de la funcin ser llamado
durante la destruccin del objeto? Por lo tanto, el compilador y el vinculador imponen la
existencia del cuerpo de la funcin para el destructor virtual puro.
Considere el siguiente programa:
#include <iostream>
class Base
{
public:
virtual ~Base()=0; // Pure virtual destructor
};

class Derived : public Base


{
public:
~Derived()
{
std::cout << "~Derived() is executed";
}
};

int main()
{
Base *b=new Derived();
delete b;
return 0;
}

El vinculador producir el siguiente error en el programa anterior.


Test.cpp: (. Texto $ _ZN7DerivedD1Ev [__ ZN7DerivedD1Ev] + 0x4c):
Referencia indefinida a `Base :: ~ Base () '

Ahora si la definicin para el destructor virtual puro se proporciona entonces el programa


compila y funciona muy bien.
#include <iostream>
class Base
{
public:
virtual ~Base()=0; // Pure virtual destructor
};
Base::~Base()
{
std::cout << "Pure virtual destructor is called";
}

class Derived : public Base


{
public:
~Derived()
{
std::cout << "~Derived() is executed\n";
}
};

int main()
{
Base *b = new Derived();
delete b;
return 0;
}

Salida:
~ Derived () se ejecuta
Destructor virtual puro se llama

Es importante notar que la clase se convierte en clase abstracta cuando contiene destructor
virtual puro. Por ejemplo, intente compilar el siguiente programa.
#include <iostream>
class Test
{
public:
virtual ~Test()=0; // Test now becomes abstract class
};
Test::~Test() { }

int main()
{
Test p;
Test* t1 = new Test;
return 0;
}

El programa anterior falla en la compilacin y muestra los siguientes mensajes de error.


[Error] no puede declarar la variable 'p' para ser de tipo abstracto 'Prueba'
[Nota] porque las siguientes funciones virtuales son puras dentro de 'Prueba':
[Nota] virtual Test :: ~ Test ()
[Error] no puede asignar un objeto de tipo abstracto 'Prueba'
[Nota] ya que el tipo 'Test' tiene puras funciones virtuales

Sobrecarga del operador

Sobrecarga de operador en C ++
En C ++, podemos hacer que los operadores trabajen para las clases definidas por el
usuario. Por ejemplo, podemos sobrecargar un operador '+' en una clase como String para que
podamos concatenar dos cadenas simplemente usando +.
Otras clases de ejemplo en las que se pueden sobrecargar operadores aritmticos son Nmero
Complejo, Nmero Fraccional, Nmero Entero Grande, etc.
Un ejemplo sencillo y completo
#include<iostream>
using namespace std;

class Complex {
private:
int real, imag;
public:
Complex(int r = 0, int i =0) {real = r; imag = i;}

// This is automatically called when '+' is used with


// between two Complex objects
Complex operator + (Complex const &obj) {
Complex res;
res.real = real + obj.real;
res.imag = imag + obj.imag;
return res;
}
void print() { cout << real << " + i" << imag << endl; }
};

int main()
{
Complex c1(10, 5), c2(2, 4);
Complex c3 = c1 + c2; // An example call to "operator+"
c3.print();
}

Salida:
12 + i9
Cul es la diferencia entre las funciones del operador y las funciones
normales?
Las funciones del operador son las mismas que las funciones normales. Las nicas diferencias
son, el nombre de una funcin de operador es siempre palabra clave de operador seguida de
smbolo de operador y las funciones de operador se llaman cuando se utiliza el operador
correspondiente.
A continuacin se muestra un ejemplo de funcin de operador global.
#include<iostream>
using namespace std;

class Complex {
private:
int real, imag;
public:
Complex(int r = 0, int i =0) {real = r; imag = i;}
void print() { cout << real << " + i" << imag << endl; }

// The global operator function is made friend of this class so


// that it can access private members
friend Complex operator + (Complex const &, Complex const &);
};

Complex operator + (Complex const &c1, Complex const &c2)


{
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}

int main()
{
Complex c1(10, 5), c2(2, 4);
Complex c3 = c1 + c2; // An example call to "operator+"
c3.print();
return 0;
}

Podemos sobrecargar a todos los operadores?


Casi todos los operadores pueden estar sobrecargados excepto pocos. A continuacin se
muestra la lista de operadores que no pueden sobrecargarse.
. (punto)
::
?:
tamao de
Por qu no puedo. (Dot), ::,?: Y sizeof sobrecargado?
Ver esto para las respuestas del propio Stroustrup.
Puntos importantes sobre la sobrecarga del operador
1) Para que funcione la sobrecarga del operador, al menos uno de los operandos debe ser un
objeto de clase definido por el usuario.
2) Operador de Asignacin: El compilador crea automticamente un operador de
asignacin por defecto con cada clase. El operador de asignacin predeterminado asigna todos
los miembros del lado derecho al lado izquierdo y funciona bien la mayora de los casos (este
comportamiento es igual que el constructor de copia).Vea esto para ms detalles.
3) Operador de conversin: Tambin podemos escribir operadores de conversin que
se pueden utilizar para convertir un tipo a otro tipo.
#include <iostream>
using namespace std;
class Fraction
{
int num, den;
public:
Fraction(int n, int d) { num = n; den = d; }
// conversion operator: return float value of fraction
operator float() const {
return float(num) / float(den);
}
};

int main() {
Fraction f(2, 5);
float val = f;
cout << val;
return 0;
}

Salida:
0,4
Los operadores de conversin sobrecargados deben ser un mtodo de miembro. Otros
operadores pueden ser el mtodo member o el mtodo global.
4) Cualquier constructor que se puede llamar con un nico argumento funciona como un
constructor de conversin, significa que tambin se puede utilizar para la conversin implcita a
la clase que se est construyendo.
#include<iostream>
using namespace std;

class Point
{
private:
int x, y;
public:
Point(int i = 0, int j = 0) {
x = i; y = j;
}
void print() {
cout << endl << " x = " << x << ", y = " << y;
}
};

int main() {
Point t(20, 20);
t.print();
t = 30; // Member x of t becomes 30
t.print();
return 0;
}

Salida:
X = 20, y = 20
X = 30, y = 0
Pronto estaremos discutiendo la sobrecarga de algunos operadores importantes como nuevo,
eliminar, coma, llamada de funcin, flecha, etc.

Constructor de copia vs operador de asignacin en


C ++
Grado de dificultad: Novato
Considere el siguiente programa en C ++.
#include<iostream>

#include<stdio.h>

using namespace std;

class Test

public:

Test() {}

Test(const Test &t)

cout<<"Copy constructor called "<<endl;

Test& operator = (const Test &t)

cout<<"Assignment operator called "<<endl;

};

int main()

Test t1, t2;

t2 = t1;

Test t3 = t1;

getchar();

return 0;

Salida:
Operador de asignacin llamado
constructor de copia llama
Constructor de copia se llama cuando se crea un nuevo objeto a partir de un objeto
existente, como una copia del objeto existente (ver este G-Realidad). Y operador
de asignacin se llama cuando un objeto que ya est iniciado se le asigna un nuevo
valor de otro objeto existente.
t2 = t1; // calls assignment operator, same as "t2.operator=(t1);"

Test t3 = t1; // calls copy constructor, same as "Test t3(t1);"

Cundo debemos escribir nuestro propio


operador de asignacin en C ++?
La respuesta es la misma que constructor de copia. Si una clase no contiene
punteros, entonces no hay necesidad de escribir operador de asignacin y
constructor de copia. El compilador crea un constructor de copia por defecto y los
operadores de asignacin para cada clase.El compilador creado copia constructor y
operador de asignacin puede no ser suficiente cuando tenemos punteros o
cualquier asignacin de tiempo de ejecucin de recurso como identificador de
archivo, un connection..etc red. Por ejemplo, considere el siguiente programa.
#include<iostream>

using namespace std;

// A class without user defined assignment operator

class Test

int *ptr;

public:

Test (int i = 0) { ptr = new int(i); }

void setValue (int i) { *ptr = i; }

void print() { cout << *ptr << endl; }

};

int main()

Test t1(5);

Test t2;

t2 = t1;

t1.setValue(10);

t2.print();

return 0;

Salida del programa anterior es "10". Si echamos un vistazo a main (), modificamos
't1' utilizando la funcin setValue (), pero los cambios tambin se reflejan en el
objeto 'T2'. Este tipo de cambios inesperados causa problemas.
Puesto que no hay definido por el usuario operador de asignacin en el programa
anterior, el compilador crea un operador de asignacin por defecto, que copia 'PTR'
de derecha a izquierda lado. As que de ambos 'PTR comienzan apuntando en la
misma ubicacin.
Podemos manejar el problema anterior de dos maneras.
1) No permitir la asignacin de un objeto a otro objeto. Podemos crear nuestro
propio operador de asignacin maniqu y hacerlo de forma annima.
2) Escriba su operador de asignacin que se hace copia en profundidad.
Lo mismo es cierto para el constructor de copia.
Lo que sigue es un ejemplo de la sobrecarga de operador de asignacin para la
clase anterior.
#include<iostream>

using namespace std;

class Test

int *ptr;

public:

Test (int i = 0) { ptr = new int(i); }

void setValue (int i) { *ptr = i; }

void print() { cout << *ptr << endl; }

Test & operator = (const Test &t);

};

Test & Test::operator = (const Test &t)

// Check for self assignment

if(this != &t)

*ptr = *(t.ptr);

return *this;

int main()

Test t1(5);

Test t2;

t2 = t1;
t1.setValue(10);

t2.print();

return 0;

Salida

Tambin hay que aadir un constructor de copia para la clase anterior, por lo que
las declaraciones como "Prueba = t3 t4;" tambin no causan ningn problema.
Tenga en cuenta la condicin de si en el operador de asignacin. Mientras que la
sobrecarga de operador de asignacin, hay que comprobar si hay asignacin de uno
mismo. De lo contrario, la asignacin de un objeto a s mismo puede conducir a
resultados inesperados (Ver este ). Comprobar la asignacin de auto no es
necesario para la clase de los de arriba 'Test', porque 'PTR' siempre apunta a un
entero y que puede volver a utilizar la misma memoria. Pero, en general, es una
prctica recomendada de hacer verificacin de auto-asignacin.

Cules son los operadores que no pueden ser


sobrecargados en C ++?
En C ++, los siguientes operadores no pueden ser sobrecargados:
. (Acceso miembros o Dot operador)
:? (Operador ternario o condicional)
:: (Operador de Resolucin)
. * (Puntero a miembro del operador)
sizeof (Objeto Operador tamao)
typeid (tipo de objeto del operador)

El uso de palabras clave explcito en C ++


Predecir la salida del siguiente programa en C ++.
#include <iostream>

using namespace std;

class Complex

private:

double real;

double imag;
public:

// Default constructor

Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}

// A method to compare two Complex numbers

bool operator == (Complex rhs) {

return (real == rhs.real && imag == rhs.imag)? true : false;

};

int main()

// a Complex object

Complex com1(3.0, 0.0);

if (com1 == 3.0)

cout << "Same";

else

cout << "Not Same";

return 0;

Salida: El programa compila bien y produce siguiente salida.

Mismo

Como se discuti en este GFact , en C ++, si una clase tiene un constructor que se
puede llamar con un solo argumento, a continuacin, este constructor se convierte
en constructor de conversin debido a un constructor de este tipo permite la
conversin del nico argumento a la clase que se est construyendo.
Podemos evitar este tipo de conversiones implcitas, ya que pueden conducir a
resultados inesperados. Podemos hacer que el constructor explcito con la ayuda
de palabras clave explcita . Por ejemplo, si tratamos el siguiente programa que
utiliza la palabra clave explcita con el constructor, obtenemos error de compilacin.
#include <iostream>

using namespace std;


class Complex

private:

double real;

double imag;

public:

// Default constructor

explicit Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}

// A method to compare two Complex numbers

bool operator== (Complex rhs) {

return (real == rhs.real && imag == rhs.imag)? true : false;

};

int main()

// a Complex object

Complex com1(3.0, 0.0);

if (com1 == 3.0)

cout << "Same";

else

cout << "Not Same";

return 0;

Salida: error del compilador

no puede competir con 'operador ==' en 'com1 == 3.0e + 0'

Todava podemos encasillar los valores dobles a lo complejo, pero ahora tenemos
que encasillado explcitamente. Por ejemplo, el siguiente programa funciona bien.
#include <iostream>
using namespace std;

class Complex

private:

double real;

double imag;

public:

// Default constructor

explicit Complex(double r = 0.0, double i = 0.0) : real(r), imag(i) {}

// A method to compare two Complex numbers

bool operator== (Complex rhs) {

return (real == rhs.real && imag == rhs.imag)? true : false;

};

int main()

// a Complex object

Complex com1(3.0, 0.0);

if (com1 == (Complex)3.0)

cout << "Same";

else

cout << "Not Same";

return 0;

Salida: El programa compila bien y produce siguiente salida.

Mismo

Se hereda el operador de asignacin?


En C ++, al igual que otras funciones, la funcin de operador de asignacion se
hereda de la clase derivada.
Por ejemplo, en el siguiente programa, la funcin de operador de asignacin de la
clase base se puede acceder mediante el objeto de clase derivada.
#include<iostream>

using namespace std;

class A {

public:

A & operator= (A &a) {

cout<<" base class assignment operator called ";

return *this;

};

class B: public A { };

int main()

B a, b;

a.A::operator=(b); //calling base class assignment operator function

// using derived class

getchar();

return 0;

Salida: base del operador asignacin de clase llamado

Operador de asignacin por defecto y Referencias


Hemos hablado de la sobrecarga de operador de asignacin de los recursos
asignados dinmicamente aqu . Se trata de una extensin del post anterior.En el
post anterior , hemos discutido que cuando no escribimos nuestro propio operador
de asignacin, compilador creado operador de asignacin hace copia superficial y
que ocasionan problemas. Qu pasa cuando tenemos referencias en nuestra clase
y no hay definida por el usuario operador de asignacin. Por ejemplo, predecir la
salida del siguiente programa.
#include<iostream>
using namespace std;

class Test

int x;

int &ref;

public:

Test (int i):x(i), ref(x) {}

void print() { cout << ref; }

void setX(int i) { x = i; }

};

int main()

Test t1(10);

Test t2(20);

t2 = t1;

t1.setX(40);

t2.print();

return 0;

Salida:

Error del compilador: miembro de referencia 'int & Test :: ref' no esttico,

no puede utilizar el operador de asignacin por defecto

Compilador no crea operador de asignacin por defecto en los siguientes casos


1. clase tiene un miembro de datos no esttico de un tipo const o un tipo de
referencia
2. clase tiene un miembro de datos no esttico de un tipo que tiene un operador de
asignacin de copia inaccesible
3. clase se deriva de una clase base con un operador de asignacin de copia
inaccesible
Cuando cualquiera de las condiciones anteriores es cierto, el usuario debe definir
operador de asignacin. Por ejemplo, si aadimos un operador de asignacin al
cdigo anterior, el cdigo funciona bien sin ningn error.
#include<iostream>

using namespace std;


class Test

int x;

int &ref;

public:

Test (int i):x(i), ref(x) {}

void print() { cout << ref; }

void setX(int i) { x = i; }

Test &operator = (const Test &t) { x = t.x; return *this; }

};

int main()

Test t1(10);

Test t2(20);

t2 = t1;

t1.setX(40);

t2.print();

return 0;

Salida:

10

Pre-incremento (o decremento previo) en C ++


En C ++, pre-incremento (o decremento previo) se pueden utilizar como l-valor ,
pero de incremento posterior (o post-decremento) no se pueden utilizar como l-
valor.
Por ejemplo, tras programa imprime a = 20 (++ A se utiliza como valor L)
#include<stdio.h>

int main()
{

int a = 10;

++a = 20; // works

printf("a = %d", a);

getchar();

return 0;

Y el programa siguiente falla en la compilacin con el error "no-valor-I en la


asignacin" (a ++ se utiliza como valor L)
#include<stdio.h>

int main()

int a = 10;

a++ = 20; // error

printf("a = %d", a);

getchar();

return 0;

Los punteros inteligentes en C ++


Considere el siguiente cdigo en C ++ simple con punteros normales.
MyClass *ptr = new MyClass();

ptr->doSomething();

// Debemos hacer delete (ptr) para evitar fugas de memoria

El uso de punteros inteligentes , podemos hacer que los punteros para trabajar en
forma que no es necesario llamar explcitamente a eliminar. Puntero inteligente es
una clase contenedora ms de un puntero con el operador como * y ->
sobrecargado. Los objetos de la clase puntero inteligente se ven como puntero,
pero se pueden hacer muchas cosas que un puntero normal no puede gustar la
destruccin automtica (s, nosotros no tenemos que utilizar explcitamente
eliminar), el recuento de referencias y ms.
La idea es hacer una clase con un puntero, destructor y operadores
sobrecargados como * y ->. Desde destructor se llama automticamente cuando un
objeto sale del mbito, la memoria dinmica alloicated sera eliminado
automticamente (o recuento de referencia se puede disminuir). Considere la
siguiente clase simple smartPtr.
#include<iostream>
using namespace std;

class SmartPtr

int *ptr; // Actual pointer

public:

// Constructor: Refer http://www.geeksforgeeks.org/g-fact-93/

// for use of explicit keyword

explicit SmartPtr(int *p = NULL) { ptr = p; }

// Destructor

~SmartPtr() { delete(ptr); }

// Overloading dereferencing operator

int &operator *() { return *ptr; }

};

int main()

SmartPtr ptr(new int());

*ptr = 20;

cout << *ptr;

// We don't need to call delete ptr: when the object

// ptr goes out of scope, destructor for it is automatically

// called and destructor does delete ptr.

return 0;

Salida:

20

Podemos escribir una clase puntero inteligente que funciona para todos los
tipos?
S, podemos utilizar las plantillas para escribir una clase puntero inteligente
genrico. Despus de cdigo C ++ demuestra la misma.
#include<iostream>

using namespace std;

// A generic smart pointer class

template <class T>

class SmartPtr

T *ptr; // Actual pointer

public:

// Constructor

explicit SmartPtr(T *p = NULL) { ptr = p; }

// Destructor

~SmartPtr() { delete(ptr); }

// Overloading dereferncing operator

T & operator * () { return *ptr; }

// Overloding arrow operator so that members of T can be accessed

// like a pointer (useful if T represents a class or struct or

// union type)

T * operator -> () { return ptr; }

};

int main()

SmartPtr<int> ptr(new int());

*ptr = 20;

cout << *ptr;

return 0;

Salida:

20

punteros inteligentes son tambin tiles en la gestin de los recursos, tales como
identificadores de archivo o conectores de red.
C ++ bibliotecas proporcionan implementaciones de punteros inteligentes en forma
de auto_ptr , unique_ptr , shared_ptr y weak_ptr

La sobrecarga de corriente de insercin


(<<) y las empresas de extraccin
(>>) en C ++
En C ++, operador de insercin corriente "<<" se utiliza para la produccin y el
operador de extraccin ">>" se utiliza para la entrada.
Debemos saber siguientes cosas antes de empezar la sobrecarga de estos
operadores.
1) cout es un objeto de la clase ostream y cin es una clase de objeto istream
2) Estos operadores deben ser sobrecargados como una funcin global. Y si
queremos que puedan acceder a los miembros de datos privados de clase, hay que
hacerlas amigo.
Por qu estos operadores deben ser sobrecargados como global?
En la sobrecarga de operadores, si un operador est sobrecargado como miembro,
entonces debe ser un miembro del objeto en el lado izquierdo del operador. Por
ejemplo, considere la declaracin "ob1 + ob2" (dejar que ob1 y Ob2 ser objeto de
dos clases diferentes). Para hacer esta compilacin declaracin, hay que
sobrecargar '+' en la clase de 'OB1' o hacer una funcin global '+'.
Los operadores '<<' y '<<' se denominan como "tribunal << ob1 'y' cin << ob1
'. As que si queremos hacer de ellos un mtodo de la barra, entonces deben
hacerse miembros de ostream y IStream clases, que no es una buena opcin la
mayor parte del tiempo. Por lo tanto, estos operadores estn sobrecargados como
funciones globales con dos parmetros, cout y objeto de la clase definida por el
usuario. Lo que sigue es completo programa en C ++ para demostrar la sobrecarga
de los operadores << y >>.
#include <iostream>

using namespace std;

class Complex

private:

int real, imag;

public:

Complex(int r = 0, int i =0)

{ real = r; imag = i; }

friend ostream & operator << (ostream &out, const Complex &c);

friend istream & operator >> (istream &in, Complex &c);

};

ostream & operator << (ostream &out, const Complex &c)


{

out << c.real;

out << "+i" << c.imag << endl;

return out;

istream & operator >> (istream &in, Complex &c)

cout << "Enter Real Part ";

in >> c.real;

cout << "Enter Imagenory Part ";

in >> c.imag;

return in;

int main()

Complex c1;

cin >> c1;

cout << "The complex object is ";

cout << c1;

return 0;

Salida:
Introduzca real Parte 10

Introduzca Imagenory Parte 20

El objeto complejo es 10 + i20

La sobrecarga de operadores subndice o ndice de


la matriz [] en C ++
Hemos introducido la sobrecarga de operadores . En este post sobrecarga del
operador de ndice [] se discute.
A continuacin se presentan algunos datos tiles sobre la sobrecarga de [].
1) La sobrecarga de los [] puede ser til cuando queremos comprobar si el ndice
fuera de lmite.
2) Hay que volver por referencia en funcin porque una expresin como "arr [i]" se
puede utilizar un valor izquierdo.
A continuacin se presenta programa en C ++ para demostrar la sobrecarga del
operador de ndice de matriz [].
// Overloading operators for Array class

#include<iostream>

#include<cstdlib>

using namespace std;

// A class to represent an integer array

class Array

private:

int *ptr;

int size;

public:

Array(int *, int);

// Overloading [] operator to access elements in array style

int &operator[] (int);

// Utility function to print contents

void print() const;

};

// Implementation of [] operator. This function must return a

// refernce as array element can be put on left side

int &Array::operator[](int index)

if (index >= size)

cout << "Array index out of bound, exiting";

exit(0);
}

return ptr[index];

// constructor for array class

Array::Array(int *p = NULL, int s = 0)

size = s;

ptr = NULL;

if (s != 0)

ptr = new int[s];

for (int i = 0; i < s; i++)

ptr[i] = p[i];

void Array::print() const

for(int i = 0; i < size; i++)

cout<<ptr[i]<<" ";

cout<<endl;

// Driver program to test above methods

int main()

int a[] = {1, 2, 4, 5};

Array arr1(a, 4);

arr1[2] = 6;

arr1.print();

arr1[8] = 6;

return 0;

}
Salida:
1 2 5 6

El ndice de matriz fuera de lmite, saliendo

Plantillas

Las plantillas en C ++
Plantilla es simple ya la vez muy poderosa herramienta en C ++. La simple idea es
pasar el tipo de datos como un parmetro de forma que no es necesario escribir
mismo cdigo para diferentes tipos de datos.Por ejemplo, una compaa de
software puede necesitar sort () para diferentes tipos de datos. En lugar de escribir
y mantener los cdigos mltiples, podemos escribir una clase () y pase el tipo de
datos como un parmetro.
C ++ aade dos nuevas palabras clave para apoyar las
plantillas: 'plantilla' y 'typename' . La segunda palabra clave siempre se puede
sustituir por la palabra clave "clase".
Cmo Plantillas de trabajo?
Las plantillas se utilizarn, bajo tiempo de compilador. Esto es como macros. La
diferencia es, compilador comprobacin de tipos antes de la expansin de la
plantilla. La idea es simple, contiene el cdigo fuente nica funcin / clase, pero el
cdigo compilado puede contener mltiples copias de la misma funcin / clase.

Las plantillas de funcin escribimos una funcin genrica que se puede utilizar
para diferentes tipos de datos. Ejemplos de plantillas de funcin son sort (), max (),
min (), printArray ()
#include <iostream>

using namespace std;

// One function works for all data types. This would work
// even for user defined types if operator '>' is overloaded

template <typename T>

T myMax(T x, T y)

return (x > y)? x: y;

int main()

cout << myMax<int>(3, 7) << endl; // Call myMax for int

cout << myMax<double>(3.0, 7.0) << endl; // call myMax for double

cout << myMax<char>('g', 'e') << endl; // call myMax for char

return 0;

Salida:
7

gramo

Clase plantillas Como plantillas de funcin, plantillas de clase son tiles cuando
una clase define algo que es independiente del tipo de datos. Puede ser til para las
clases como LinkedList, BinaryTre, Pila, Cola, Array, etc.
Lo que sigue es un ejemplo sencillo de clase de plantilla de matriz.
#include <iostream>

using namespace std;

template <typename T>

class Array {

private:

T *ptr;

int size;

public:

Array(T arr[], int s);

void print();

};
template <typename T>

Array<T>::Array(T arr[], int s) {

ptr = new T[s];

size = s;

for(int i = 0; i < size; i++)

ptr[i] = arr[i];

template <typename T>

void Array<T>::print() {

for (int i = 0; i < size; i++)

cout<<" "<<*(ptr + i);

cout<<endl;

int main() {

int arr[5] = {1, 2, 3, 4, 5};

Array<int> a(arr, 5);

a.print();

return 0;

Salida:
1 2 3 4 5

Puede haber ms de un argumentos a las plantillas?


S, al igual que los parmetros normales, podemos pasar ms de un tipo de datos
como argumentos a las plantillas. El siguiente ejemplo demuestra la misma.
#include<iostream>

using namespace std;

template<class T, class U>

class A {

T x;

U y;
Public:

A() { cout<<"Constructor Called"<<endl; }

};

int main() {

A<char, char> a;

A<int, double> b;

return 0;

Salida:
constructor Llamado

constructor Llamado

Se puede especificar el valor predeterminado de argumentos de plantilla?


S, al igual que los parmetros normales, podemos especificar los argumentos por
defecto a las plantillas. El siguiente ejemplo demuestra la misma.
#include<iostream>

using namespace std;

template<class T, class U = char>

class A {

public:

T x;

U y;

A() { cout<<"Constructor Called"<<endl; }

};

int main() {

A<char> a; // This will call A<char, char>

return 0;

Ejecutar en el IDE

Salida:
constructor Llamado

Cul es la diferencia entre la sobrecarga de funciones y plantillas?


Tanto la sobrecarga de funciones y las plantillas son ejemplos de polimorfismo
funcin de programacin orientada a objetos. La sobrecarga de funciones se utiliza
cuando mltiples funciones hacen operaciones similares, las plantillas se utilizan
cuando mltiples funciones hacen operaciones idnticas.
Qu pasa cuando no hay miembro esttico en una clase de plantilla /
funcin?
Cada instancia de una plantilla contiene su propia variable esttica. Ver plantillas
y las variables estticas para ms detalles.
Qu es la plantilla de la especializacin?
Especializacin de plantilla nos permite disponer de cdigo diferente para un
determinado tipo de datos. Ver Plantilla de especializacin para ms detalles.
Podemos pasar parmetros no tipolgicas a las plantillas?
Podemos pasar argumentos que no sean de tipo de plantillas. Parmetros de tipo
no se utilizan principalmente para especificar los valores mximo y mnimo o
cualquier otro valor constante para un caso particular de plantilla. Lo importante a
tener en cuenta sobre los parmetros de tipo no es, deben ser const. Compilador
debe conocer el valor de los parmetros no de tipo en tiempo de
compilacin. Debido compilador necesita para crear funciones / clases por un valor
que no sea tipo especificado en tiempo de compilacin. En el programa de abajo, si
sustituimos 10000 o 25 con una variable, obtenemos error del compilador. Por
favor, vea este .
A continuacin se muestra un programa en C ++.

// A C++ program to demonstrate working of non-type

// parameters to templates in C++.

#include <iostream>

using namespace std;

template <class T, int max>

int arrMin(T arr[], int n)

int m = max;

for (int i = 0; i < n; i++)

if (arr[i] < m)

m = arr[i];

return m;

int main()

int arr1[] = {10, 20, 15, 12};

int n1 = sizeof(arr1)/sizeof(arr1[0]);
char arr2[] = {1, 2, 3};

int n2 = sizeof(arr2)/sizeof(arr2[0]);

// Second template parameter to arrMin must be a constant

cout << arrMin<int, 10000>(arr1, n1) << endl;

cout << arrMin<char, 256>(arr2, n2);

return 0;

Salida:
10

Plantilla Metaprogramacin en C ++
Predecir la salida del siguiente programa en C ++.
#include <iostream>

using namespace std;

template<int n> struct funStruct

enum { val = 2*funStruct<n-1>::val };

};

template<> struct funStruct<0>

enum { val = 1 };

};

int main()

cout << funStruct<8>::val << endl;

return 0;

}
Salida:

256

El programa calcula "2 aumento de la potencia 8 (o 2 ^ 8)". De hecho, la


estructura funStruct se puede utilizar para calcular 2 ^ n para cualquier n conocido
(o constante n). Lo especial de programa anterior es: clculo se realiza en
tiempo de compilacin . Por lo tanto, es compilador que calcula 2 ^ 8. Para
entender cmo este compilador, consideremos los siguientes hechos acerca de las
plantillas y las enumeraciones:
1) Podemos pasar no tipolgicas parmetros (parmetros que no son tipos de
datos) a las plantillas de clase / funcin.
2) Al igual que otras expresiones const, valores de las constantes de enumeracin
son en evaluados en tiempo de compilacin.
3) Cuando el compilador ve un nuevo argumento a una plantilla, compilador crea
una nueva instancia de la plantilla.

Vamos a echar un vistazo ms de cerca el programa original. Cuando compilador


ve funStruct <8> :: val , se trata de crear una instancia de funStruct con el
parmetro como 8, resulta que funStruct <7> tambin debe ser creado como
enumaration constante val debe ser evaluado en tiempo de
compilacin. Para funStruct <7> , compilador necesita funStruct <6> y as
sucesivamente. Por ltimo, compilador utiliza funStruct <1> :: val y el tiempo de
compilacin recursin termina. Por lo tanto, el uso de plantillas, podemos escribir
programas que hacen el clculo en tiempo de compilacin, estos programas se
denominan metaprograms plantilla . Metaprogramming plantilla es de hecho Turing
completo , lo que significa que cualquiera puede expresar cmputo mediante un
programa informtico puede ser calculado, de alguna forma, por un metaprograma
plantilla. Plantilla Metaprogramacin generalmente no se utiliza en programas
prcticos, es un conecpt interesante.

La especializacin de plantillas de C ++
Plantillas de C ++ es una caracterstica. Escribimos cdigo una vez y lo usamos
para cualquier tipo de datos que incluye los tipos de datos definidos por el
usuario. Por ejemplo, sort () puede ser escrita y se utiliza para ordenar ningn
artculo de tipos de datos.Una pila clase puede ser creado que se puede utilizar
como una pila de cualquier tipo de datos.
Y si queremos un cdigo diferente para un determinado tipo de
datos? Considere la posibilidad de un gran proyecto que necesita una funcin de
clasificacin () para las matrices de muchos diferentes tipos de datos. Deje rpido
Ordenado ser utilizado para todos los tipos de datos excepto carbn. En el caso de
la trucha, los valores totales posibles son 256 y contando tipo pueden ser una
mejor opcin. Es posible que el cdigo utilizado slo cuando sort () se llama para
el tipo de datos char?
Es posible en C ++ para conseguir un comportamiento especial para
un tipo de datos particular. Esto se llama especializacin de plantilla.
// A generic sort function

template <class T>


void sort(T arr[], int size)

// code to implement Quick Sort

// Template Specialization: A function specialized for char data type

template <>

void sort<char>(char arr[], int size)

// code to implement counting sort

Otro ejemplo podra ser una clase Conjunto que representa un conjunto de
elementos y soporta operaciones como unin, interseccin, etc. Cuando el tipo de
elementos es char, es posible que queramos usar un simple matriz booleana de
tamao 256 para hacer un conjunto. Para otros tipos de datos, tenemos que usar
alguna otra tcnica compleja.

Un ejemplo de programa para la funcin de plantilla


especializacin
Por ejemplo, considere el siguiente cdigo sencillo en el que nos hemos divertido
plantilla general () para todos los tipos de datos excepto int. Para int, hay una
versin especializada de diversin ().
#include <iostream>

using namespace std;

template <class T>

void fun(T a)

cout << "The main template fun(): " << a << endl;

template<>

void fun(int a)

cout << "Specialized Template for int type: " << a << endl;

int main()
{

fun<char>('a');

fun<int>(10);

fun<float>(10.14);

Ejecutar en el IDE

Salida:

La principal diversin plantilla (): una

Plantilla especializada para el tipo int: 10

La diversin plantilla principal (): 10.14

Un programa de ejemplo para la plantilla de clase de


especializacin
En el siguiente programa, una versin especializada de clase Test est escrito para
el tipo de datos int.
#include <iostream>

using namespace std;

template <class T>

class Test

// Data memnbers of test

public:

Test()

// Initializstion of data memnbers

cout << "General template object \n";

// Other methods of Test

};

template <>

class Test <int>

public:

Test()

{
// Initializstion of data memnbers

cout << "Specialized template object\n";

};

int main()

Test<int> a;

Test<char> b;

Test<float> c;

return 0;

Salida:

objeto de plantilla especializada

objeto general de plantilla

objeto general de plantilla

Cmo funciona la plantilla de la especializacin?


Cuando escribimos de cualquier modo de plantilla o de clase, el compilador crea
una copia de esa funcin / clase cada vez compilador ve que se utiliza para un
nuevo tipo de datos o nuevo conjunto de tipos de datos (en caso de mltiples
argumentos de plantilla).
Si una versin especializada est presente, compilador comprueba en primer lugar
con la versin especializada y, a continuacin la plantilla principal. Compilador
comprueba primero con la versin ms especializada, haciendo coincidir el
parmetro que se pasa con el tipo (s) de datos especificado en una versin
especializada.

LOS ESPACIOS DE NOMBRES

Espacio de nombres en C ++ | Set 1


(Introduccin)
Considere la siguiente programa en C ++.
// A program to demonstrate need of namespace
int main()
{
int value;
value = 0;
double value; // Error here
value = 0.0;
}
salida:

Error del compilador:

"Valor" tiene una declaracin anterior como "valor int '

En cada mbito, un nombre slo puede representar una entidad. Por lo tanto, no
puede haber dos variables con el mismo nombre en el mismo mbito. El uso de
espacios de nombres, podemos crear dos variables o funciones miembros que
tienen el mismo nombre.
// Here we can see that more than one variables
// are being used without reporting any error.
// That is because they are declared in the
// different namespaces and scopes.
#include <iostream>
using namespace std;

// Variable created inside namespace


namespace first
{
int val = 500;
}

// Global variable
int val = 100;

int main()
{
// Local variable
int val = 200;

// These variables can be accessed from


// outside the namespace using the scope
// operator ::
cout << first::val << '\n';

return 0;
}

Salida:
500

Definicin y creacin:

Los espacios de nombres nos permiten entidades con nombre de grupo que de otro
modo tendran un alcance global en los mbitos ms estrechos, dndoles espacio
de nombres alcance . Esto permite a la organizacin de los elementos de los
programas en diferentes mbitos lgicos mencionados por nombres.
Espacio de nombres es una caracterstica aadida en C ++ y no est presente
en C.
Un espacio de nombres es una regin declarativa que proporciona un margen
para los identificadores (nombres de los tipos, la funcin, las variables, etc.)
dentro de ella.
Se permiten mltiples bloques de espacio de nombres con el mismo

nombre. Todas las declaraciones dentro de esos bloques se declaran en el
mbito con nombre.
Una definicin de espacio de nombres comienza con la palabra clave espacio de
nombres seguido del nombre de espacio de nombres de la siguiente manera:
namespace_name espacio de nombres

int x, y; // declaraciones cdigo donde

// X e y son declarados en

// Alcance de namespace_name

declaraciones de espacios aparecen slo en el mbito global.


declaraciones de espacios se pueden anidar dentro de otro espacio de
nombres.
declaraciones de espacios no tienen especificadores de acceso. (Pblica o
privada)
No hay necesidad de dar punto y coma despus de la llave de cierre de la
definicin de espacio de nombres.
Podemos dividir la definicin de espacio de nombres durante varias unidades.
// Creating namespaces
#include <iostream>
using namespace std;
namespace ns1
{
int value() { return 5; }
}
namespace ns2
{
const double x = 100;
double value() { return 2*x; }
}

int main()
{
// Access value function within ns1
cout << ns1::value() << '\n';

// Access value function within ns2


cout << ns2::value() << '\n';

// Access variable x directly


cout << ns2::x << '\n';

return 0;
}

Salida:
5

200

100
Clases y Espacio de nombres:

Lo que sigue es una forma sencilla de crear clases en un espacio de nombres


// A C++ program to demonstrate use of class
// in a namespace
#include <iostream>
using namespace std;

namespace ns
{
// A Class in a namespace
class geek
{
public:
void display()
{
cout << "ns::geek::display()\n";
}
};
}

int main()
{
// Creating Object of student Class
ns::geek obj;

obj.display();

return 0;
}

Salida:

ns :: friki :: display ()

Clase tambin puede ser declarada en el interior del espacio de nombres y


definido fuera del espacio de nombres utilizando la sintaxis siguiente
// A C++ program to demonstrate use of class
// in a namespace
#include <iostream>
using namespace std;

namespace ns
{
// Only declaring class here
class geek;
}

// Defining class outside


class ns::geek
{
public:
void display()
{
cout << "ns::geek::display()\n";
}
};
int main()
{
//Creating Object of student Class
ns::geek obj;
obj.display();
return 0;
}

Salida:

ns :: friki :: display ()

Podemos definir mtodos tambin fuera del espacio de nombres . El siguiente


es un ejemplo de cdigo.
// A C++ code to demonstrate that we can define
// methods outside namespace.
#include <iostream>
using namespace std;

// Creating a namespace
namespace ns
{
void display();
class geek
{
public:
void display();
};
}

// Defining methods of namespace


void ns::geek::display()
{
cout << "ns::geek::display()\n";
}
void ns::display()
{
cout << "ns::display()\n";
}

// Driver code
int main()
{
ns::geek obj;
ns::display();
obj.display();
return 0;
}

Salida:
ns :: display ()

ns :: friki :: display ()

Вам также может понравиться