Clase que no se pueda instanciar c++

Z

Como puedo hacer que una clase no se pueda instanciar??debo poner el constructor por defecto y el constructor de copia como privado o que debería hacer??

dagavi

Lo típico que he visto en Java es lo que indicas: constructores como privados. Supongo que en C++ se podrá hacer lo mismo (nunca lo he probado ni he visto algo similar).

Edit: Probado:

Al hacer un "A a;" con constructor privado (si está público funciona ok):

prog.cpp: En la función ‘int main()’:
prog.cpp:8:9: error: ‘A::A()’ es privado
prog.cpp:13:7: error: desde este contexto

Xxee

Crea una clase abstracta, con métodos del palo virtual void function() = 0;

1 1 respuesta
r2d2rigo

El metodo para tener clases abstractas en el caso de C++ es como dice #3, la clase tiene que tener al menos una funcion virtual pura: http://en.wikibooks.org/wiki/C%2B%2B_Programming/Classes/Abstract_Classes/Pure_Abstract_Classes

dagavi

Pero no está pidiendo una clase abstracta. Aunque visualmente pueda dar el mismo resultado, no es lo mismo:

Una clase abstracta no se puede instanciar nunca, pero está pensado para el polimorfismo (las subclases están obligadas a implementar el método), no para estas guarradas O_o. Igualmente, haces una subclase con "function() {}" y listo, ya puedes instanciar tu "subclase" (que no añade nada)

Una clase con constructores privados podría instanciarse por un método constructor. Es la típica implementación de un singleton: Constructores privados + método estático (y público) getInstance() que, al ser un método de la clase, si puede instanciar el objeto.

1 respuesta
Spacelord

No he probado nunca a privatizar un constructor, pero, aunque lo hagas, ¿no generaría por defecto la clase con el constructor vacío? ¿O en C++ el compilador no hace eso ( soy más de Java y de C++ no controlo demasiado)?

Si digo una burrada decídmelo, que esto no lo había visto en mi vida.

1 respuesta
r2d2rigo

#5 Obviamente sin saber para que quiere #1 una clase que no pueda instanciarse no sabemos si lo que quiere es un singleton o una abstracta, pero viendo como ha formulado la pregunta yo he entendido que lo que quiere es lo segundo.

#6 El compilador solo crea el constructor por defecto (sin parametros) si tu no lo declaras, asi que si lo haces privado ya no existe ese problema.

2 respuestas
Spacelord

#7 Aaahmigo, vale, ya me queda claro. Thx.

Z

ok haré una clase abstracta

1 respuesta
dagavi

#7 Pero poner un constructor como privado no es un singletón (cosa que deduzco de "no sabemos si quiere un singleton o una clase abstracta" ). Poner un constructor como privado es eso: impedir llamar al constructor / impedir instanciar.

1 respuesta
r2d2rigo

#10 pero a ver, una clase con constructor privado vale para estas cosas:

-Singleton.
-Abstracta pura.
-Estatica pura.

Dime tu para que la quiere si no.

1 1 respuesta
dagavi

#11 No se a que te refieres con que una clase con un constructor privado sirve para tener una abstracta pura. Una abstracta pura que declara pero no define algún método, obligando a definirlo a las subclases (e impidiendo la instanciación de la superclase, pero porque es una clase que no define todos sus métodos, simplemente). Pero tal vez haya otras cosas con mismo nombre que no conozca (muy posible) :O

Pero bueno, una posible utilización: un mecanismo de creación de objetos controlados vía un método "crearInstancia()" que pueda decidir no crear. Un ejemplo simple podría ser permitir crear 100 instancias (es lo primero que se me ha ocurrido, obviamente veo que es algo raro poner el constructor privado)

1 respuesta
r2d2rigo

#12 definir un constructor privado en una clase abstracta es necesario si esa clase va a tener variables miembro que necesitan algun tipo de inicializacion. Igualmente, declarando privado el constructor por defecto, obligas a las clases derivadas a declarar uno: http://stackoverflow.com/a/8513537/575432

La utilizacion que dices viene a ser un factory de toda la vida ( http://en.wikipedia.org/wiki/Factory_method_pattern#C.23 ), pero esto viene a ser segun gustos. Yo la implementacion que te acabo de linkar la veo una tonteria completa, preferiria declarar constructores con distintos parametros sin tener que usar metodos estaticos que devuelvan una instancia nueva.

cabron

#9

Es que como tampoco dices que quieres hacer exactamente, no sé que decirte, pero lo que estás haciendo no tiene pinta de tener mucho sentido, ¿vas a hacer una clase abstracta, pero no por que sea abstracta de verdad (no vas a derivar subclases de ella), si no solamente para que no se instancie? ¿Y como la vas a usar, con métodos estáticos?

En C++ una clase que solo tienen métodos estáticos y que está pensada para no ser instanciada de ninguna forma posible, no tiene ningún sentido (o yo no se lo veo), por que C++ permite tener funciones que no pertenecen a ninguna clase.

Si realmente vas a hacer una clase con un puñado de métodos estáticos y nada más, para eso crea directamente funciones sueltas que estén todas declaradas en el mismo archivo de cabecera, ¿qué ventaja te aporta una clase?

Si es solo por que necesitas variables locales que mantengan su valor entre llamadas, lo puedes hacer sin una clase (declara la variable como static), y si necesitas variables compartidas entre las distintas funciones, también lo puedes hacer sin una clase, declara la variable en el mismo archivo de cabecera donde están las funciones como si fuese global, pero también utiliza static para declararla.

1
Z

Estas son las clases

#ifndef ARTICULO_H
#define ARTICULO_H
#include "fecha.h"
#include "cadena.h"
#include <iomanip>
#include <set>
#include "autor.h"
class Articulo{
	public:
		typedef std::set<Autor*> Autores;
		Articulo(Autores aut,Cadena ref,Cadena t,Fecha f,double p);
		Cadena referencia()const;
		Cadena titulo()const;
		Fecha f_publi()const;
		double precio()const;
		double& precio();
	protected:
		Autores autores;
		Cadena referencia_;
		Cadena titulo_;
		Fecha f_publi_;
		double precio_;
		
};
ostream& operator<<(ostream& s,const Articulo& art);
ostringstream& operator<<(ostringstream& s,const Articulo& art);
#endif

#ifndef ARTICULO_ALMACENABLE_H
#define ARTICULO_ALMACENABLE_H

class ArticuloAlmacenable:public Articulo{
	public:
        ArticuloAlmacenable(Autores aut,Cadena ref,Cadena t,Fecha f,double p,unsigned st);	
		unsigned stock()const;
		unsigned& stock();
	protected:
		unsigned stock_;
};
#endif

#ifndef LIBRO_H
#define LIBRO_H
class Libro:public ArticuloAlmacenable{
	public:
		Libro(Autores aut,Cadena ref,Cadena t,Fecha f,double p,unsigned st,unsigned pag);
		unsigned n_pag()const;
	private:
		unsigned n_pag_;
};
#endif

#ifndef CEDERRON_H
#define CEDERRON_H
class Cederron:public ArticuloAlmacenable{
	public:
		Cederron(Autores aut,Cadena ref,Cadena t,Fecha f,double p,unsigned st,unsigned tama);
		unsigned tam()const;
	private:
		unsigned tam_;
};
#endif

#ifndef LIBRODIGITAL_H
#define LIBRODIGITAL_H
#include "fecha.h"

class LibroDigital:public Articulo{
	public:
		LibroDigital(Autores aut,Cadena ref,Cadena t,Fecha f,double p,unsigned st,Fecha fx);
		Fecha f_expir()const;
	private:
		Fecha f_expir_;
};
#endif

De ellas Articulo y ArticuloAlmacenable debe ser abstractas,tengo que reescribir el operador de inserción ( << ) para esta clase de forma que se elija durante la ejecución la definición adecuada dentro de la jerarquía de clases y no se que metodo debo declarar virtual para que las clases sean abstractas ni como debo reescribir los operadores de inserción ,ando un poco perdido :wtf:

1 respuesta
cabron

#15

En la superclase Articulo declara este método:

public:
virtual void operator<< (Articulo* articulo)=0;

Obviamente lo de void y Articulo* cambialo por los tipos que necesites

En articulo almacenable no hace falta que lo añadas por que también es abstracta y no vas a implementarlo, y no necesitas declararlo otra vez ya que lo hereda de Artículo.

En las clases Libro, Cederron y Libro digital es donde le darías una implementación a ese método:

public:
virtual void operator<< (Articulo* articulo)
{
//lo que vayas a hacer
}

1 respuesta
Z

#16 No seria asi?
public:
virtual void operator<< (std::ostream& os)=0;
El articulo lo obtendria con this y lo que necesito es el objeto de la clase ostream no??y declarando el operador << miembro de la clase .

1 respuesta
cabron

#17

Eso depende de lo que quieras hacer, yo te he puesto un tipo por poner algo, por que no sabía para que lo quieres. Lo normal cuando se sobreescribe un operador es no cambiar la semántica del operador, por que solo sirve para que el código sea confuso, por ejemplo sobreescribir el operador "+" para que reste en lugar sumar no es la mejor de las ideas...

En este caso el operador << tiene dos usos, sirve para operaciones de bits, y sirve para la entrada y salida estándar, y tú lo quieres sobreescribir para el segundo uso, y en ese caso es un pelín más complicado.

El motivo es que cuando lo vas a usar normalmente primero va tu objeto y luego el operador:

miObjeto << miParametro

Mi objeto lo recibes con this como has comentado, y miParametro es lo que vas a recibir como parámetro de la función operator<< ()

El problema viene en que cuando lo usas para la salida estándar, es al revés, tu objeto va es el parámetro y va después del operador:

cout << miObjeto

Y para hacerlo más complicado, parte del comportamiento estándar de este operador es encadenar su uso, y no deberías quitar ese comportamiento:

cout << miObjeto << "\n" << miOtroObjeto

Para esto hay dos detalles, primero, no puede ser void, si no que debes devolver un objeto de tipo ostream, para que se pueda volver a usar el operador.

El segundo detalle es que ya no puedes tener un método virtual y miembro de las clase Articulo y sus subclases, por que piensa que al hacer cout << miArticulo, realmente ejecutas el operador desde cout, no desde tu clase. La solución es sobreescribir el operador de formal global:

ostream& operator << (ostream &os, Articulo &articulo)
{

articulo.blabla()

return os;

}

De esa forma cuando se ejecute el operador << usando como parámetros un objeto ostream (cout) y un objeto de tu clase Articulo, se ejecuta lo que hayas puesto en ese método. Además devuelves otra vez el objeto ostream lo que te permite hacer las llamadas en cadena con <<

Y para encajar esto con lo de lo que cada subtipo tenga su propia implementación, um, no sé, ahí me pillas un poco, lo único que se me ocurre es que el método que uses para extraer los datos del artículo (lo que seria blabla() ), sea un método virtual=0 en la clase artículo, y este sobrescrito en las subclases.

1 respuesta
Z

#18 Como tambien tengo que hacer en articulo un metodo que muestre los valores de los atributos propios de cada subclase habia pensado en dejar el operador << tal como esta y dentro de el llamar a este metodo que he dicho,el cual estaria declarado como virtual en articulo
e impelmentadrlo en las subclases.
Una duda que tengo es que si declaro este metodo virtual puro en articulo,si no lo implemento en articuloAlmacenable,esta tambien sera abstracta?

1 respuesta
cabron

#19

El operador << no te queda más remedio que sobrecargarlo fuera de la clase en una función global por lo que te comenté en el otro post, en todo caso dentro de la implementación de la función global llamas a un método que se virtual void mostrarDatos()=0 o algo parecido.

Sobre la otra pregunta, sí, así es, si un método abstracto no es implementado en una clase derivada, ésta pasa a ser también abstracta.

Z

Ahora me surgió un problema en el constructor de la clase pedido, concretamente en el bucle para recorrer el map para el cual definí tipo, typedef std::map<Articulo*,unsigned> Artículos,concretamente al hacer f(it->first->stock()<it->second),ahora el tipo articulo no tiene un elemento llamado stock,si no que el stock lo tienen los elementos de la subclase ArticuloAlmacenable
intente hacer un dynamic_cast para saber si it->first es un articulo almacenable pero me seguía avisando que la clase articulo no tenia un miembro llamado stock.

Pedido::Pedido(Usuario_Pedido& up,Pedido_Articulo& pa,Usuario& u,const Tarjeta& t,const Fecha& f):num_(++numPedido),tarjeta_(&t),fecha_(f){
	if(u.compra().size()==0)
		throw Vacio(u);
	if(tarjeta_->titular()!=&u)
		throw Impostor(u);
	if(tarjeta_->caducidad()<Fecha()){
		Fecha f=tarjeta_->caducidad();
		throw Tarjeta::Caducada(f);
	}
	for(Usuario::Articulos::const_iterator it=u.compra().begin();it!=u.compra().end();++it){
		if(it->first->stock()<it->second){
			//std::cout<<"first="<<it->first->stock()<<"   second ="<<it->second<<std::endl;
			//throw SinStock(*(it->first));
		}
		else
			it->first->stock()=it->first->stock()-it->second;
		pa.pedir(*this,*(it->first),it->first->precio(),it->second);
		total_=total_+((it->first->precio())*(it->second));
	}
	up.asocia(u,*(this));
	up.asocia(*(this),u);
	//u.compra().clear();
}
Z

Como creo que no me explique bien ,lo explico de nuevo centrándome en este bucle dentro del constructor

 for(Usuario::Articulos::const_iterator it=u.compra().begin();it!=u.compra().end();++it){
                if(it->first->stock()<it->second){
                        //std::cout<<"first="<<it->first->stock()<<"   second ="<<it->second<<std::endl;
                        //throw SinStock(*(it->first));
                }
                else
                        it->first->stock()=it->first->stock()-it->second;
                pa.pedir(*this,*(it->first),it->first->precio(),it->second);
                total_=total_+((it->first->precio())*(it->second));
        }

Como puedo distinguir si el puntero que esta almacenado en first es de un articuloAlmacenable o no lo es ,para hacer comparar el stock si lo es y no hacerlo si no lo es

shaba

Creo que sólo utilizas el dynamic cast para comprobar el if y luego utilizas el ítem del mapa que es de la clase base

int main () {
  try {
    CBase * pba = new CDerived;
    CBase * pbb = new CBase;
    CDerived * pd;

pd = dynamic_cast<CDerived*>(pba);
if (pd==0) cout << "Null pointer on first type-cast" << endl;

pd = dynamic_cast<CDerived*>(pbb);
if (pd==0) cout << "Null pointer on second type-cast" << endl;

  } catch (exception& e) {cout << "Exception: " << e.what();}
  return 0;
}

Si sigues este ejemplo de código extraído de http://www.cplusplus.com/doc/tutorial/typecasting/ no deberías tener ningún problema

Z

Estoy intentando hacer esto pero me da error al llamar a erase, la clase Fecha es una clase mia que tiene sobrecargado el operador < para comparar 2 fechas y f_expir() es una funcion de librodigital que devuelve un atributo de tipo fecha

if(LibroDigital* ld=dynamic_cast<LibroDigital*>(it->first)){
			Fecha hoy;
			if(ld->f_expir()<hoy){
				u.compra().erase(ld);
			}
		}

Usuarios habituales