#c #oop #inheritance #interface
#c #ооп #наследование #интерфейс
Вопрос:
Есть ли у класса интерфейса какой-либо способ принудительно использовать определение конструктора копирования и, возможно, других конструкторов? В моем случае у меня есть IResource
чисто абстрактный класс, и я хочу, чтобы все классы, реализующие этот интерфейс, определяли copy-constr, конструктор для загрузки из файла и конструктор для загрузки из памяти.
Комментарии:
1. Почему это важно? Вы все равно не можете обеспечить, чтобы ресурс был фактически загружен из файла? Насколько я могу видеть, наличие одинаковых конструкторов действительно было бы необходимо, если вы создаете вещи в шаблоне. Который уже должен решить вашу проблему. — Что касается конструктора копирования, вы, вероятно, предпочли бы метод clone, хотя я сомневаюсь, что вы можете принудительно использовать класс, не производный напрямую от интерфейса, для его реальной реализации.
2. …? Это важно, потому что я хочу убедиться, что все мои ресурсы предоставляют программисту одинаковый интерфейс и ведут себя согласованно.
3.
//All implementations of IResource must have a publicly accessible copy constructor; violators of this rule will be terminated
должно сработать4. -1: Любой, кто пытается это сделать, ставит телегу впереди лошади. Вы используете наследование не по назначению, а по полиморфному поведению во время выполнения. Если вам не нужен полиморфизм во время выполнения, тогда вам следует использовать композицию, а не наследование.
Ответ №1:
Для того, чтобы создать объект, вам нужно знать конкретный класс для использования (как бы иначе он узнал, сколько памяти выделить, или какую виртуальную таблицу использовать и т.д.?). Как таковой, интерфейс не задействован при работе с конструкторами, и вы не можете использовать интерфейсы (чистые виртуальные) для обеспечения существования такого конструктора. Если подумать, вполне естественно, что виртуалы работают только тогда, когда у вас есть полиморфный объект, то есть после создания экземпляра. Любой, кто ссылается на ваш интерфейс IResource, будет иметь дело только с созданными объектами и никогда не коснется конструктора.
Вы можете применить такого рода ограничения к материалам, используя шаблоны, если хотите. Просто вызывая конструктор копирования из шаблонной функции, компилятор будет жаловаться, если он столкнется с созданием экземпляра шаблона с использованием типа, у которого нет конструктора копирования.
Комментарии:
1. Обратите внимание, что «вызов конструктора копирования» означает, что вам придется использовать CRTP.
2. @Billy: Да, или, возможно, какой-то вариант, включающий еще больше шаблонов.
Ответ №2:
Вы не можете применить это, и это тоже было бы неправильным способом. Напротив, вам следует запретить использование общедоступных конструкторов копирования в полиморфной иерархии классов…
struct IResource {
virtual IResource* Clone() const = 0;
virtual ~IResource() {}
};
Разработчик IResource
должен следовать этому шаблону:
class ConcreteResource : public IResource, public boost::noncopyable { // or equivalent
public:
virtual ConcreteResource* Clone() const;
explicit ConcreteResource(std::string const amp; pString) : mString(pString) {}
private:
std::string mString;
};
ConcreteResource* ConcreteResource::Clone() const {
return new ConcreteResource(this->mString);
}
Ответ №3:
Что-то в вашем проекте использует IResource
абстрактный класс, и почему-то я сомневаюсь, что это требует, чтобы используемые им объекты содержали определенные конструкторы.
Что-то еще создает IResource
объекты (возможно, множество вещей) и для этого должно использовать конструктор. Создаваемые конкретные классы должны реализовывать необходимые конструкторы, иначе код не будет компилироваться.
Итак, ответ на ваш вопрос заключается в том, что вы обеспечиваете наличие конструкторов, используя эти конструкторы в каком-либо другом коде для создания объектов. Имейте в виду, что если конструкторы нигде не используются, они не нужны.
Комментарии:
1. Я не согласен. Иногда лучше предоставить некоторые функции, даже если вы не уверены, что собираетесь их использовать, просто потому, что их существование подразумевается чем-то другим. Например, если вы определяете
operator>
, вам следует также определитьoperator<
, даже если вы не собираетесь его использовать. В моем случае у меня есть функции для загрузки ресурса из файла fom или из памяти; ожидается, что также будет возможно загрузить ресурс непосредственно из конструктора, если вы того пожелаете.2. @Paul: На самом деле, вы не должны. Вместо этого вы должны использовать стандартные операторы отношений . И Дэниел прав — все, что вы пишете, должно, по крайней мере, использоваться в модульном тестировании, и желательно больше, чем это.
3. @Paul: Проблема в отношении этого вопроса, однако, заключается в том, разумно ли для интерфейса предъявлять такое требование. Интерфейс существует для того, чтобы указывать, как конкретный контекст использует свои объекты, а не как они были изначально созданы.
Ответ №4:
вы можете выдвинуть все требования к реализациям ресурсов следующим образом:
class t_resource_interface {
protected:
virtual ~t_resource_interface();
public:
virtual t_serialization* serializeResource() = 0;
virtual t_thing* cloneResource() = 0;
};
/* type disambiguators */
typedef enum t_load_from_url { LoadFromURL = 0 } t_load_from_url;
typedef enum t_load_from_memory { LoadFromMemory = 0 } t_load_from_memory;
typedef enum t_copy_constructor { CopyConstructor = 0 } t_copy_constructor;
template < typename TResourceImplementation >
class t_resource : public t_resource_interface {
public:
/* copy ctor should generally be avoided due to the expense. introduce a parameter for those cases where it's really needed and disable the standard copy ctor */
t_resource(const t_copy_constructoramp; copyCtor, const t_resourceamp; other) : t_resource_interface(), d_implementation(TResourceImplementation::CopyConstructor(other.d_implementation)) {
MONUnusedParameter(copyCtor);
}
t_resource(const t_load_from_urlamp; fromFile, const t_urlamp; url) : t_resource_interface(), d_implementation(TResourceImplementation::LoadFromURL(url)) {
MONUnusedParameter(fromFile);
}
t_resource(const t_load_from_memoryamp; fromMemory, const t_serializationamp; serialization) : t_resource_interface(), d_implementation(TResourceImplementation::LoadFromMemory(serialization)) {
MONUnusedParameter(fromMemory);
}
virtual ~t_resource() {
}
public:
/* t_resource_interface requirements. implementation forwarded to TResourceImplementation */
virtual t_serialization* serializeResource() {
return this->d_implementation->serializeResource();
}
virtual t_thing* cloneResource() {
return this->d_implementation->cloneResource();
}
private:
/* assuming you will end up needing dynamic allocation/polymorphism along the way... */
t_auto_pointer<TResourceImplementation> d_implementation;
private:
/* prohibited */
t_resource(const t_resourceamp;);
t_resourceamp; operator=(const t_resourceamp;);
};
class t_image_resource_implementation : public t_resource_interface {
private:
static t_image_resource_implementation* ValidationCheck(const t_image_resource_implementation* const arg) {
assert(arg amp;amp; "allocation or argument error");
if (0 == arg) {
return 0;
}
else if (0 == arg->isValid()) {
delete res;
return 0;
}
else {
return arg;
}
}
public:
static t_image_resource_implementation* CopyConstructor(const t_image_resource_implementation* const other) {
return ValidationCheck(new t_image_resource_implementation(other, ...));
}
static t_image_resource_implementation* LoadFromURL(const t_urlamp; url) {
/* assuming t_image_at_url_resource_implementation exists */
return ValidationCheck(new t_image_at_url_resource_implementation(url, ...));
}
static t_image_resource_implementation* LoadFromMemory(const t_serializationamp; serialization) {
assert(serialization);
if (0 == serialization) {
return 0;
}
else {
return ValidationCheck(new t_image_resource_implementation(serialization, ...));
}
}
/* some physical ctors and the rest of the implementation... */
public:
/* t_resource_interface requirements */
virtual t_serialization* serializeResource() {
return this->createSerialization();
}
virtual t_thing* cloneResource() {
return this->clone();
}
};
typedef t_resource<t_image_resource_implementation> t_image_resource;
t_error_code ConvertImageToGrayscale(const t_urlamp; sourceUrl, const t_urlamp; destinationUrl) {
t_image_resource imageResource(LoadFromURL, sourceUrl);
/* ... */
}