secondo me è una brutta pratica inserire nella classe base metodi usati solo da alcune classi derivate, non è un errore, io stesso lo faccio talvolta se mi sembra che sia più pratico. Le cose belle sono belle ma non sempre pratiche, la domanda da porsi in fase di progetto è : se aggiungessi un altro sottotipo tutti i mie algoritmi continueranno a funzionare senza dovere essere modificati? Se si allora qualcosa non va, altrimenti è tutto ok anche se non bello, alla fine dobbiamo produrre per guadagnare e portare a casa il pane, non fare arte.

In generale ogni volta che nel tuo codice aggiungi un IF per qualche tipo vuol dire che c'è rischio di spaghettizzazione del codice, con conseguente necessità di ORE di lavoro per ogni minima modifica e quindi perderci il guadagno prefissato... per questo bisogna cercare di limitare al massimo l'uso di IF (di questo genere)... senza però diventare paranoici.

Nel tuo caso mi pare che il principale errore (se così si può definire) sia che confondi la struttura logica del programma con la struttura dei dati in input, oppure che fai derivare la struttura del programma dalla struttura dei dati.

In realtà, la fase di input (parsing/deserializing, chiamiamola come vuoi) è quella in cui vengono istanziati gli oggetti ed è l'unica che deve essere consapevole (facendo degli IF) di che cosa istanziare sulla base del formato della stringa in input, per cui se mai dovessi aggiungere un nuovo sottotipo, l'unico IF aggiuntivo andrà inserito nel parser di input. (In output un metodo Serialize(stream * out) ti eviterà di usare un IF aggiuntivo)

Venendo al dunque, suppongo di avere un oggetto di interfaccia grafica che usa dei dati che leggi dall'input. Suppongo che se un determinato tag = 1 si tratti di un triangolo, se tag=0 si tratti di altri dati, sui triangoli voglio potere calcolare l'area su tutto voglio potere stampare la lista dei nomi degli oggetti.

un caso del genere potrebbe essere impementato cosi
codice:
class oggetto_base
{
     virtual string nome() = 0;
     string Serialize();
}

classe geometria_base : oggetto_base
{
     virtual float area();
}

classe A:oggetto_base
{...}

classe triangolo:geometria_base{}


class graphic_interface
{
       oggetto_base * oggetti;
       geometria_base * oggetti_geometrici;
	parser(inputstream)
	{
	       se nType=1 {
	             istanzio un oggetto di tipo triangolo, 
	             lo inserisco nella lista degli oggetti geometrici
	             lo inserisco nella lista degli oggetti 
	        }           
	       se ntype=0 
		{
	       		allora istanzio un oggetto di tipo standard
			lo inserisco nella lista degli oggetti
		}
	}

	float calcolaPerimetro()
	{
		per ogni elemento in oggetti_geometrici
                area += elemento->Area();
	}
        void stampalista()
        {
		per ogni elemento in oggetti
                cout << oggetto->Nome();
        }
        
}
se domani vuoi aggiungere il tipo RETTANGOLO, devi solo modificare PARSE e basta.