Почему мы не можем объявить std:: vector?
Потратив довольно много времени на разработку в 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, которая может помочь мне обойти это ?
Спасибо, что прочитали это !
Энтони
7 ответов:
вы не можете создавать экземпляры абстрактных классов, поэтому вектор абстрактных классов не может работать.
однако, вы можете использовать вектор указателей на абстрактные классы:
std::vector<IFunnyInterface*> ifVec;Это также позволяет фактически использовать полиморфное поведение - даже если класс не был абстрактным, хранение по значению приведет к проблеме объект нарезки.
вы не можете создать вектор абстрактного типа класса, потому что вы не можете создавать экземпляры абстрактного класса и контейнеры стандартной библиотеки C++, такие как std::vector store values (т. е. экземпляры). Если вы хотите сделать это, вы должны создать вектор указателей на абстрактный тип класса.
ваш workround не будет работать, потому что виртуальные функции (именно поэтому вы хотите, чтобы абстрактный класс В первую очередь) работают только при вызове через указатели или ссылки. Вы также невозможно создать векторы ссылок, поэтому это вторая причина, по которой вы должны использовать вектор указателей.
вы должны понимать, что C++ и C# имеют очень мало общего. Если вы собираетесь изучать C++, вы должны думать об этом, как начать с нуля, и прочитать хороший выделенный учебник по C++, такой как Ускоренный C++ Кениг и Му.
традиционная альтернатива-использовать
vectorуказателей, как уже отмечалось.для тех, кто ценит,
Boostпоставляется с очень интересной библиотекой:Pointer Containersкоторый идеально подходит для этой задачи и освобождает вас от различных проблем, подразумеваемых указателями:
- управление временем жизни
- двойное разыменование итераторов
отметим, что это значительно лучше, чем
vectorсмарт-указатели, как с точки зрения производительности и интерфейса.теперь есть 3-й вариант, который должен изменить вашу иерархию. Для лучшей изоляции пользователя, я видел несколько раз следующий шаблон используется:
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идиома обогащена aStrategyузор.это работает, конечно, только в том случае, когда вы не хотите манипулировать "истинными" объектами напрямую, а предполагаете глубокое копирование. Так что это может быть не будь тем, кем хочешь.
В этом случае мы не можем использовать даже этот код:
std::vector <IFunnyInterface*> funnyItems;или
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;потому что нет связи между FunnyImpl и IFunnyInterface и нет неявного преобразования между FUnnyImpl и IFunnyInterface из-за частного наследования.
вы должны обновить ваш код следующим образом:
class IFunnyInterface { public: virtual void IamFunny() = 0; }; class FunnyImpl: public IFunnyInterface { public: virtual void IamFunny() { cout << "<INSERT JOKE HERE>"; } };
потому что для изменения размера вектора вам нужно использовать конструктор по умолчанию и размер класса, который в свою очередь требует, чтобы он был конкретным.
вы можете использовать указатель как другие предложили.
std:: vector будет пытаться выделить память, чтобы содержать ваш тип. Если ваш класс является чисто виртуальным, вектор не может знать размер класса, который он должен будет выделить.
Я думаю, что с вашим решение, вы сможете составить
vector<IFunnyInterface>но вы не сможете манипулировать FunnyImpl внутри него. Например, если IFunnyInterface (абстрактный класс) имеет размер 20 (я действительно не знаю), а FunnyImpl имеет размер 30, потому что у него больше членов и кода, Вы в конечном итоге попытаетесь чтобы вписать 30 в ваш вектор 20решение было бы выделить память в куче с "новой" и хранить указатели в
vector<IFunnyInterface*>
Я думаю, что основной причиной этого действительно печального ограничения является тот факт, что конструкторы не могут быть виртуальными. Компилятор не может генерировать код, который скопирует объект, не зная его времени во время компиляции.
Comments