C : интерфейс, обеспечивающий выполнение определения copy-constr

#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);
    /* ... */
}