Nachdem ich einige Zeit in C # verbracht hatte, stellte ich fest, dass Sie, wenn Sie eine abstrakte Klasse deklarieren, um sie als Schnittstelle zu verwenden, keinen Vektor dieser abstrakten Klasse instanziieren können, um Instanzen der untergeordneten Klassen zu speichern.
#pragma once
#include <iostream>
#include <vector>
using namespace std;
class IFunnyInterface
{
public:
virtual void IamFunny() = 0;
};
class FunnyImpl: IFunnyInterface
{
public:
virtual void IamFunny()
{
cout << "<INSERT JOKE HERE>";
}
};
class FunnyContainer
{
private:
std::vector <IFunnyInterface> funnyItems;
};
Die Zeile, die den Vektor der abstrakten Klasse deklariert, verursacht diesen Fehler in MS VS2005:
error C2259: 'IFunnyInterface' : cannot instantiate abstract class
Ich sehe eine offensichtliche Problemumgehung, die darin besteht, IFunnyInterface durch Folgendes zu ersetzen:
class IFunnyInterface
{
public:
virtual void IamFunny()
{
throw new std::exception("not implemented");
}
};
Ist dies eine akzeptable Problemumgehung für C ++? Wenn nicht, gibt es eine Drittanbieter-Bibliothek wie Boost, die mir helfen könnte, dies zu umgehen?
Vielen Dank für das Lesen!
Anthony
quelle
Sie können keinen Vektor eines abstrakten Klassentyps erstellen, da Sie keine Instanzen einer abstrakten Klasse und C ++ - Standardbibliothekscontainer wie std :: vector-Speicherwerte (dh Instanzen) erstellen können. Wenn Sie dies tun möchten, müssen Sie einen Vektor von Zeigern auf den abstrakten Klassentyp erstellen.
Ihr Workround würde nicht funktionieren, da virtuelle Funktionen (weshalb Sie die abstrakte Klasse an erster Stelle haben möchten) nur funktionieren, wenn sie über Zeiger oder Referenzen aufgerufen werden. Sie können auch keine Referenzvektoren erstellen. Dies ist ein zweiter Grund, warum Sie einen Zeigervektor verwenden müssen.
Sie sollten erkennen, dass C ++ und C # sehr wenig gemeinsam haben. Wenn Sie C ++ lernen möchten, sollten Sie sich vorstellen, dass Sie bei Null anfangen, und ein gutes dediziertes C ++ - Tutorial wie Accelerated C ++ von Koenig und Moo lesen .
quelle
In diesem Fall können wir nicht einmal diesen Code verwenden:
oder
Da zwischen FunnyImpl und IFunnyInterface keine IS A-Beziehung besteht und aufgrund der privaten Vererbung keine implizite Konvertierung zwischen FUnnyImpl und IFunnyInterface erfolgt.
Sie sollten Ihren Code wie folgt aktualisieren:
quelle
Die traditionelle Alternative ist die Verwendung eines
vector
Zeigers, wie bereits erwähnt.Für diejenigen, die es zu schätzen wissen, gibt es
Boost
eine sehr interessante Bibliothek: SiePointer Containers
ist perfekt für die Aufgabe geeignet und befreit Sie von den verschiedenen Problemen, die Zeiger mit sich bringen:Beachten Sie, dass dies
vector
sowohl hinsichtlich der Leistung als auch der Benutzeroberfläche erheblich besser ist als bei intelligenten Zeigern.Jetzt gibt es eine dritte Alternative, die darin besteht, Ihre Hierarchie zu ändern. Zur besseren Isolierung des Benutzers habe ich mehrmals das folgende verwendete Muster gesehen:
Dies ist recht einfach und eine Variation der
Pimpl
Redewendung, die durch einStrategy
Muster angereichert wird .Dies funktioniert natürlich nur in dem Fall, in dem Sie die "wahren" Objekte nicht direkt bearbeiten möchten, und erfordert eine Tiefenkopie. Es kann also nicht das sein, was Sie wünschen.
quelle
Um die Größe eines Vektors zu ändern, müssen Sie den Standardkonstruktor und die Größe der Klasse verwenden, was wiederum erfordert, dass er konkret ist.
Sie können einen Zeiger wie vorgeschlagen verwenden.
quelle
std :: vector versucht, Speicher zuzuweisen, der Ihren Typ enthält. Wenn Ihre Klasse rein virtuell ist, kann der Vektor die Größe der Klasse, die er zuordnen muss, nicht kennen.
Ich denke, dass Sie mit Ihrer
vector<IFunnyInterface>
Problemumgehung in der Lage sein werden, eine zu kompilieren, aber Sie werden FunnyImpl darin nicht manipulieren können. Wenn beispielsweise IFunnyInterface (abstrakte Klasse) die Größe 20 hat (ich weiß es nicht wirklich) und FunnyImpl die Größe 30 hat, weil es mehr Mitglieder und Code hat, werden Sie am Ende versuchen, 30 in Ihren Vektor von 20 zu passenDie Lösung wäre, Speicher auf dem Heap mit "neu" zuzuweisen und Zeiger darin zu speichern
vector<IFunnyInterface*>
quelle
Ich denke, dass die Hauptursache für diese wirklich traurige Einschränkung die Tatsache ist, dass Konstruktoren nicht virtuell sein können. Der Compiler kann keinen Code generieren, der das Objekt kopiert, ohne seine Zeit in der Kompilierungszeit zu kennen.
quelle