Академический Документы
Профессиональный Документы
Культура Документы
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
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())
int f ( int x) {
return x+10;
}
int main() {
getchar();
return 0;
}
int main() {
getchar();
return 0;
}
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;
Salida:
Error de compilador: redefinicin de 'void fun (int)'
int main()
{
const char *ptr = "GeeksforGeeks";
fun(ptr);
return 0;
}
Salida:
Const fun () GeeksforGeeks
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;
}
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));
}
}
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
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;
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
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
};
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>
int main()
{
int *n = new int(10); // initialization with new()
cout<<*n;
getchar();
return 0;
}
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;
getchar();
return 0;
}
Clase y Objeto
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
};
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.
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;
}
class Base {
public:
int x;
};
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;
};
int main()
{
Derived d;
d.x = 20; // works fine becuase inheritance is public
getchar();
return 0;
}
class Test {
static Test self; // works fine
};
int main()
{
Test t;
getchar();
return 0;
}
class Test {
Test * self; //works fine
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>
class Test {
Test self; // Error
};
int main()
{
Test t;
getchar();
return 0;
}
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
class Empty { };
int main()
{
cout << sizeof(Derived);
return 0;
}
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 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
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;
}
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
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 '
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;
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.
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);
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;
}
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.
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;
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 main()
{
Point p1(10, 15); // constructor is called here
return 0;
}
Salida:
P1.x = 10, p1.y = 15
int main()
{
Point p1(10, 15); // first constructor is called here
Point p2; // Second constructor is called here
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; }
return 0;
}
Salida:
P1.x = 10, p1.y = 15
P2.x = 10, p2.y = 15
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
};
int main()
{
String str1("GeeksQuiz");
String str2 = str1;
str2.change("GeeksforGeeks");
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
};
int main()
{
String str1("GeeksQuiz");
String str2 = str1;
str2.change("GeeksforGeeks");
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/
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;
}
class myInteger
{
private:
int value;
Programa 2
#include<iostream>
class myInteger
{
private:
int value;
public:
myInteger(int v) // parametrized constructor
{ value = v; }
int main()
{
myInteger I1;
getchar();
return 0;
}
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;
}
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 main() {
Point t1(10, 15);
cout<<"x = "<<t1.getX()<<", ";
cout<<"y = "<<t1.getY();
return 0;
}
/* OUTPUT:
x = 10, y = 15
*/
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
*/
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
*/
class A {
int i;
public:
A(int );
};
A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}
int main() {
B obj(10);
return 0;
}
/* OUTPUT:
A's Constructor called: Value of i: 10
B's Constructor called
*/
class A {
int i;
public:
A(int );
};
A::A(int arg) {
i = arg;
cout << "A's Constructor called: Value of i: " << i << endl;
}
int main() {
B obj(10);
return 0;
}
class A {
int i;
public:
A(int );
int getI() const { return i; }
};
int main() {
A a(10);
cout<<a.getI();
return 0;
}
/* OUTPUT:
10
*/
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.
#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;
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;
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>
int main()
{
// create an object
Test *ptr = new Test;
return 0;
}
int i;
class A
{
public:
~A()
{
i=10;
}
};
int foo()
{
i=3;
A ob;
return i;
}
int main()
{
cout << "i = " << foo() << endl;
return 0;
}
int i;
class A
{
public:
~A()
{
i = 10;
}
};
int& foo()
{
i = 3;
A ob;
return i;
}
int main()
{
cout << "i = " << foo() << endl;
return 0;
}
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;
}
int main()
{
B ob = "copy me";
return 0;
}
La salida del programa anterior es:
Constructor llamado
int main() {
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.
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 ()
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.
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.
Base() { }
// An interface
virtual void DisplayAction() = 0;
};
~Derived1()
{
cout << "Derived1 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived1" << endl;
}
};
~Derived2()
{
cout << "Derived2 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived2" << endl;
}
};
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();
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;
// An interface
virtual void DisplayAction() = 0;
};
~Derived1()
{
cout << "Derived1 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived1" << endl;
}
};
~Derived2()
{
cout << "Derived2 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived2" << endl;
}
};
class User
{
public:
if( input == 1 )
{
pBase = new Derived1;
}
else
{
pBase = new Derived2;
}
~User()
{
if( pBase )
{
delete pBase;
pBase = 0;
}
}
private:
Base *pBase;
};
int main()
{
User *user = new User();
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
if( input == 1 )
{
pBase = new Derived1;
}
else if( input == 2 )
{
pBase = new Derived2;
}
else
{
pBase = new Derived3;
}
}
~User()
{
if( pBase )
{
delete pBase;
pBase = 0;
}
}
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;
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;
}
};
~Derived2()
{
cout << "Derived2 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived2" << endl;
}
};
~Derived3()
{
cout << "Derived3 destroyed" << endl;
}
void DisplayAction()
{
cout << "Action from Derived3" << endl;
}
};
if( id == 1 )
{
return new Derived1;
}
else if( id == 2 )
{
return new Derived2;
}
else
{
return new Derived3;
}
}
//// LIBRARY END
int input;
~User()
{
if( pBase )
{
delete pBase;
pBase = 0;
}
}
delete user;
}
class Base
{
public:
//
};
~Derived()
{
cout << "Derived destroyed" << endl;
}
};
int main()
{
Derived s1;
return 0;
}
~Derived1()
{
cout << "~Derived1 destroyed" << endl;
}
void ChangeAttributes()
{
cout << "Derived1 Attributes Changed" << endl;
}
Base *Clone()
{
return new Derived1(*this);
}
};
~Derived2()
{
cout << "~Derived2 destroyed" << endl;
}
void ChangeAttributes()
{
cout << "Derived2 Attributes Changed" << endl;
}
Base *Clone()
{
return new Derived2(*this);
}
};
~Derived3()
{
cout << "~Derived3 destroyed" << endl;
}
void ChangeAttributes()
{
cout << "Derived3 Attributes Changed" << endl;
}
Base *Clone()
{
return new Derived3(*this);
}
};
if( id == 1 )
{
return new Derived1;
}
else if( id == 2 )
{
return new Derived2;
}
else
{
return new Derived3;
}
}
//// LIBRARY END
int input;
cout << "Enter ID (1, 2 or 3): ";
cin >> input;
~User()
{
if( pBase )
{
delete pBase;
pBase = 0;
}
}
void Action()
{
// Duplicate current object
Base *pNewBase = pBase->Clone();
private:
Base *pBase;
};
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
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)
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
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
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
class Base
{
public:
virtual void show() { cout<<" In Base \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 */ }
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; }
};
int main()
{
C c;
return 0;
}
Salida:
El constructor de B llamado
El constructor de A llamado
El constructor de C llamado
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
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; }
};
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); }
};
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 Base {
public:
virtual int fun(int i) { }
};
int main()
{ }
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; };
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; }
};
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
Salida:
Soy objeto de clase base, i = 33
Soy objeto de clase Derivado, i = 45, j = 54
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>
class Base
{
public:
int fun()
{
cout<<"Base::fun() called";
}
int fun(int i)
{
cout<<"Base::fun(int i) 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)
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;
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
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
};
int main()
{
A a;
B x;
a.showB(x);
return 0;
}
Salida:
B :: b = 0
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;
int main()
{
show();
getchar();
return 0;
}
Funciones virtuales
int main()
{
Derived d1;
Base *bp = &d1;
bp->fun();
return 0;
}
Salida:
Derivado :: fun (), x = 0
class Base
{
public:
virtual void fun ( int x = 0)
{
cout << "Base::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)
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>
class Test
{
public:
// Error: Virtual member functions cannot be static
virtual static void fun() { }
};
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.
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>
class base {
public:
base()
{ cout<<"Constructing base \n"; }
virtual ~base()
{ cout<<"Destructing base \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
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;
}
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;
}
class Derived;
class Base {
private:
virtual void fun() { cout << "Base Fun"; }
friend int main();
};
int main()
{
Base *ptr = new Derived;
ptr->fun();
return 0;
}
Salida:
Diversin derivada ()
#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();
return 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; }
};
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;
};
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;
};
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;
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.
int main()
{
Base *b=new Derived();
delete b;
return 0;
}
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;
}
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;}
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; }
int main()
{
Complex c1(10, 5), c2(2, 4);
Complex c3 = c1 + c2; // An example call to "operator+"
c3.print();
return 0;
}
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.
#include<stdio.h>
class Test
public:
Test() {}
};
int main()
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);"
class Test
int *ptr;
public:
};
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>
class Test
int *ptr;
public:
};
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.
class Complex
private:
double real;
double imag;
public:
// Default constructor
};
int main()
// a Complex object
if (com1 == 3.0)
else
return 0;
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>
private:
double real;
double imag;
public:
// Default constructor
};
int main()
// a Complex object
if (com1 == 3.0)
else
return 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
};
int main()
// a Complex object
if (com1 == (Complex)3.0)
else
return 0;
Mismo
class A {
public:
return *this;
};
class B: public A { };
int main()
B a, b;
getchar();
return 0;
class Test
int x;
int &ref;
public:
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,
int x;
int &ref;
public:
void setX(int i) { x = i; }
};
int main()
Test t1(10);
Test t2(20);
t2 = t1;
t1.setX(40);
t2.print();
return 0;
Salida:
10
int main()
{
int a = 10;
getchar();
return 0;
int main()
int a = 10;
getchar();
return 0;
ptr->doSomething();
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
public:
// Destructor
~SmartPtr() { delete(ptr); }
};
int main()
*ptr = 20;
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>
class SmartPtr
public:
// Constructor
// Destructor
~SmartPtr() { delete(ptr); }
// union type)
};
int main()
*ptr = 20;
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
class Complex
private:
public:
{ real = r; imag = i; }
friend ostream & operator << (ostream &out, const Complex &c);
};
return out;
in >> c.real;
in >> c.imag;
return in;
int main()
Complex c1;
return 0;
Salida:
Introduzca real Parte 10
#include<iostream>
#include<cstdlib>
class Array
private:
int *ptr;
int size;
public:
Array(int *, int);
};
exit(0);
}
return ptr[index];
size = s;
ptr = NULL;
if (s != 0)
ptr[i] = p[i];
cout<<ptr[i]<<" ";
cout<<endl;
int main()
arr1[2] = 6;
arr1.print();
arr1[8] = 6;
return 0;
}
Salida:
1 2 5 6
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>
// One function works for all data types. This would work
// even for user defined types if operator '>' is overloaded
T myMax(T x, T y)
int main()
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>
class Array {
private:
T *ptr;
int size;
public:
void print();
};
template <typename T>
size = s;
ptr[i] = arr[i];
void Array<T>::print() {
cout<<endl;
int main() {
a.print();
return 0;
Salida:
1 2 3 4 5
class A {
T x;
U y;
Public:
};
int main() {
A<char, char> a;
A<int, double> b;
return 0;
Salida:
constructor Llamado
constructor Llamado
class A {
public:
T x;
U y;
};
int main() {
return 0;
Ejecutar en el IDE
Salida:
constructor Llamado
#include <iostream>
int m = max;
if (arr[i] < m)
m = arr[i];
return m;
int main()
int n1 = sizeof(arr1)/sizeof(arr1[0]);
char arr2[] = {1, 2, 3};
int n2 = sizeof(arr2)/sizeof(arr2[0]);
return 0;
Salida:
10
Plantilla Metaprogramacin en C ++
Predecir la salida del siguiente programa en C ++.
#include <iostream>
};
enum { val = 1 };
};
int main()
return 0;
}
Salida:
256
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 <>
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.
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:
class Test
public:
Test()
};
template <>
public:
Test()
{
// Initializstion of data memnbers
};
int main()
Test<int> a;
Test<char> b;
Test<float> c;
return 0;
Salida:
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;
// Global variable
int val = 100;
int main()
{
// Local variable
int val = 200;
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
// X e y son declarados en
// Alcance de namespace_name
int main()
{
// Access value function within ns1
cout << ns1::value() << '\n';
return 0;
}
Salida:
5
200
100
Clases y Espacio de nombres:
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 ()
namespace ns
{
// Only declaring class here
class geek;
}
Salida:
ns :: friki :: display ()
// Creating a namespace
namespace ns
{
void display();
class geek
{
public:
void display();
};
}
// Driver code
int main()
{
ns::geek obj;
ns::display();
obj.display();
return 0;
}
Salida:
ns :: display ()
ns :: friki :: display ()