Потратив некоторое время на разработку на C #, я заметил, что если вы объявляете абстрактный класс с целью использования его в качестве интерфейса, вы не можете создать экземпляр вектора этого абстрактного класса для хранения экземпляров дочерних классов.
#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;
};
Строка, объявляющая вектор абстрактного класса, вызывает эту ошибку в MS VS2005:
error C2259: 'IFunnyInterface' : cannot instantiate abstract class
Я вижу очевидный обходной путь - заменить IFunnyInterface следующим:
class IFunnyInterface
{
public:
virtual void IamFunny()
{
throw new std::exception("not implemented");
}
};
Является ли это приемлемым обходным решением для C ++? Если нет, то есть ли какая-нибудь сторонняя библиотека, такая как boost, которая могла бы помочь мне обойти это?
Спасибо за чтение этого !
Энтони
источник
Вы не можете создать вектор абстрактного типа класса, потому что вы не можете создавать экземпляры абстрактного класса и контейнеры стандартной библиотеки C ++, такие как std :: vector хранят значения (то есть экземпляры). Если вы хотите это сделать, вам нужно будет создать вектор указателей на абстрактный тип класса.
Ваш рабочий процесс не будет работать, потому что виртуальные функции (именно поэтому вам в первую очередь нужен абстрактный класс) работают только при вызове через указатели или ссылки. Вы также не можете создавать векторы ссылок, поэтому это вторая причина, по которой вы должны использовать вектор указателей.
Вы должны понимать, что C ++ и C # имеют очень мало общего. Если вы собираетесь изучать C ++, вам следует подумать об этом как о начале с нуля и прочитать хорошее специальное руководство по C ++, такое как Accelerated C ++ от Koenig и Moo.
источник
В этом случае мы не можем использовать даже этот код:
std::vector <IFunnyInterface*> funnyItems;
или
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Потому что нет связи IS A между FunnyImpl и IFunnyInterface и не существует неявного преобразования между FUnnyImpl и IFunnyInterface из-за частного наследования.
Вы должны обновить свой код следующим образом:
class IFunnyInterface { public: virtual void IamFunny() = 0; }; class FunnyImpl: public IFunnyInterface { public: virtual void IamFunny() { cout << "<INSERT JOKE HERE>"; } };
источник
Традиционной альтернативой является использование
vector
указателей, как уже отмечалось.Для тех, кто ценит,
Boost
поставляется с очень интересной библиотекой:Pointer Containers
она идеально подходит для данной задачи и избавляет вас от различных проблем, связанных с указателями:Обратите внимание, что это значительно лучше, чем
vector
интеллектуальные указатели, как с точки зрения производительности, так и с точки зрения интерфейса.Теперь есть третья альтернатива - изменить вашу иерархию. Для лучшей изоляции пользователя я несколько раз встречал следующую схему:
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 { .. };
Это довольно простой вариант
Pimpl
идиомы, обогащенныйStrategy
шаблоном.Это работает, конечно, только в том случае, если вы не хотите напрямую манипулировать «истинными» объектами, и включает глубокое копирование. Так что это может быть не то, что вы хотите.
источник
Потому что для изменения размера вектора вам нужно использовать конструктор по умолчанию и размер класса, который, в свою очередь, требует, чтобы он был конкретным.
Вы можете использовать указатель, как и другие варианты.
источник
std :: vector попытается выделить память для вашего типа. Если ваш класс является чисто виртуальным, вектор не может знать размер класса, который ему придется выделить.
Я думаю, что с вашим обходным путем вы сможете скомпилировать,
vector<IFunnyInterface>
но не сможете манипулировать FunnyImpl внутри него. Например, если IFunnyInterface (абстрактный класс) имеет размер 20 (я действительно не знаю), а FunnyImpl имеет размер 30, потому что у него больше членов и кода, вы в конечном итоге попытаетесь уместить 30 в свой вектор 20Решением было бы выделить память в куче «новым» и сохранить указатели в
vector<IFunnyInterface*>
источник
Я думаю, что первопричина этого действительно печального ограничения заключается в том, что конструкторы не могут быть виртуальными. Из-за этого компилятор не может сгенерировать код, который копирует объект, не зная его времени во время компиляции.
источник