Spędziwszy trochę czasu na rozwijaniu w C #, zauważyłem, że jeśli zadeklarujesz klasę abstrakcyjną w celu użycia jej jako interfejsu, nie możesz utworzyć instancji wektora tej klasy abstrakcyjnej do przechowywania instancji klas potomnych.
#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;
};
Linia deklarująca wektor klasy abstrakcyjnej powoduje ten błąd w MS VS2005:
error C2259: 'IFunnyInterface' : cannot instantiate abstract class
Widzę oczywiste obejście, które polega na zastąpieniu IFunnyInterface następującym:
class IFunnyInterface
{
public:
virtual void IamFunny()
{
throw new std::exception("not implemented");
}
};
Czy to dopuszczalne obejście C ++ mądre? Jeśli nie, czy istnieje biblioteka innej firmy, taka jak boost, która mogłaby mi pomóc w obejściu tego problemu?
Dziękuję za przeczytanie!
Anthony
źródło
Nie można utworzyć wektora typu klasy abstrakcyjnej, ponieważ nie można tworzyć instancji klasy abstrakcyjnej ani kontenerów biblioteki standardowej C ++, takich jak std :: vector przechowujące wartości (tj. Instancje). Jeśli chcesz to zrobić, będziesz musiał utworzyć wektor wskaźników do typu klasy abstrakcyjnej.
Twoje pole pracy nie zadziałałoby, ponieważ funkcje wirtualne (dlatego chcesz przede wszystkim klasy abstrakcyjnej) działają tylko wtedy, gdy są wywoływane za pomocą wskaźników lub referencji. Nie możesz również tworzyć wektorów odniesień, więc jest to drugi powód, dla którego musisz użyć wektora wskaźników.
Powinieneś zdawać sobie sprawę, że C ++ i C # mają niewiele wspólnego. Jeśli zamierzasz uczyć się C ++, powinieneś pomyśleć o tym jak o rozpoczęciu od zera i przeczytać dobry, dedykowany samouczek C ++, taki jak Accelerated C ++ autorstwa Koeniga i Moo.
źródło
W tym przypadku nie możemy użyć nawet tego kodu:
std::vector <IFunnyInterface*> funnyItems;
lub
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Ponieważ nie ma relacji IS A między FunnyImpl i IFunnyInterface i nie ma niejawnej konwersji między FUnnyImpl i IFunnyInterface ze względu na dziedziczenie prywatne.
Powinieneś zaktualizować swój kod w następujący sposób:
class IFunnyInterface { public: virtual void IamFunny() = 0; }; class FunnyImpl: public IFunnyInterface { public: virtual void IamFunny() { cout << "<INSERT JOKE HERE>"; } };
źródło
Tradycyjną alternatywą jest użycie
vector
wskaźników, jak już wspomniano.Dla tych, którzy doceniają, dostępna
Boost
jest bardzo interesująca biblioteka:Pointer Containers
która doskonale nadaje się do zadania i uwalnia od różnych problemów wynikających ze wskaźników:Zauważ, że jest to znacznie lepsze niż
vector
inteligentne wskaźniki, zarówno pod względem wydajności, jak i interfejsu.Teraz istnieje trzecia alternatywa, która polega na zmianie hierarchii. Dla lepszej izolacji użytkownika widziałem kilka razy następujący wzór:
class IClass; class MyClass { public: typedef enum { Var1, Var2 } Type; explicit MyClass(Type type); int foo(); int bar(); private: IClass* m_impl; }; struct IClass { virtual ~IClass(); virtual int foo(); virtual int bar(); }; class MyClass1: public IClass { .. }; class MyClass2: public IClass { .. };
Jest to dość proste i odmiana
Pimpl
idiomu wzbogaconaStrategy
wzorem.Działa to oczywiście tylko w przypadku, gdy nie chcesz bezpośrednio manipulować „prawdziwymi” obiektami i wymaga głębokiego kopiowania. Więc może nie być to, czego sobie życzysz.
źródło
Ponieważ aby zmienić rozmiar wektora, musisz użyć domyślnego konstruktora i rozmiaru klasy, co z kolei wymaga, aby był on konkretny.
Możesz użyć wskaźnika, jak sugerowano inne.
źródło
std :: vector spróbuje przydzielić pamięć do przechowywania Twojego typu. Jeśli twoja klasa jest czysto wirtualna, wektor nie może znać rozmiaru klasy, którą będzie musiał zaalokować.
Myślę, że dzięki twojemu obejściu będziesz mógł skompilować plik,
vector<IFunnyInterface>
ale nie będziesz w stanie manipulować w nim FunnyImpl. Na przykład, jeśli IFunnyInterface (klasa abstrakcyjna) ma rozmiar 20 (naprawdę nie wiem), a FunnyImpl ma rozmiar 30, ponieważ ma więcej członków i kodu, w końcu spróbujesz zmieścić 30 w swoim wektorze 20Rozwiązaniem byłoby przydzielenie pamięci na stercie za pomocą „nowego” i przechowywanie wskaźników w
vector<IFunnyInterface*>
źródło
Myślę, że główną przyczyną tego naprawdę smutnego ograniczenia jest fakt, że konstruktorzy nie potrafią wirtualizować. Z tego powodu kompilator nie może wygenerować kodu kopiującego obiekt bez znajomości jego czasu w czasie kompilacji.
źródło