【问题标题】:C++: Interface enforcing definition of copy-constrC ++:接口强制定义copy-constr
【发布时间】:2011-08-01 19:41:37
【问题描述】:

接口类有没有办法强制复制构造函数的定义,也许还有其他构造函数的定义?在我的例子中,我有一个IResource 纯抽象类,我希望所有实现这个接口的类都定义一个copy-constr、一个从文件加载的构造函数和一个从内存加载的构造函数。

【问题讨论】:

  • 为什么这很重要?无论如何,您不能强制实际上从文件加载资源吗?据我所知,如果您在模板中构造事物,则确实需要具有相同的构造函数。哪个应该已经解决了您的问题。 - 至于复制构造函数,你可能更想要一个克隆方法,虽然我怀疑你可以强制一个不直接从接口派生的类来实际实现它。
  • ...?这很重要,因为我想确保我的所有资源都为程序员提供相同的接口并以一致的方式运行。
  • //All implementations of IResource must have a publicly accessible copy constructor; violators of this rule will be terminated 应该可以解决问题
  • -1:任何试图这样做的人都是在把车放在马的前面。您没有将继承用于其预期目的,即运行时多态行为。如果您不需要运行时多态性,那么您应该使用组合而不是继承。

标签: c++ oop inheritance interface


【解决方案1】:

您无法强制执行,这也不是正确的方法。相反,您应该防止在多态类层次结构中使用公共复制构造函数...

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 & pString) : mString(pString) {}
private:
    std::string mString;
};

ConcreteResource* ConcreteResource::Clone() const {
    return new ConcreteResource(this->mString);
}

【讨论】:

    【解决方案2】:

    您可以将所有需求推送到资源实现,如下所示:

    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_constructor& copyCtor, const t_resource& other) : t_resource_interface(), d_implementation(TResourceImplementation::CopyConstructor(other.d_implementation)) {
            MONUnusedParameter(copyCtor);
        }
    
        t_resource(const t_load_from_url& fromFile, const t_url& url) : t_resource_interface(), d_implementation(TResourceImplementation::LoadFromURL(url)) {
            MONUnusedParameter(fromFile);
        }
    
        t_resource(const t_load_from_memory& fromMemory, const t_serialization& 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_resource&);
        t_resource& operator=(const t_resource&);
    };
    
    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 && "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_url& 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_serialization& 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_url& sourceUrl, const t_url& destinationUrl) {
        t_image_resource imageResource(LoadFromURL, sourceUrl);
        /* ... */
    }
    

    【讨论】:

      【解决方案3】:

      您的项目中的某些东西使用了 IResource 抽象类,不知何故我怀疑它是否要求它使用的对象包含特定的构造函数。

      其他东西创建 IResource 对象(可能有很多东西),为此它必须使用构造函数。创建的具体类必须实现必要的构造函数,否则代码将无法编译。

      因此,您的问题的答案是通过在其他代码中使用这些构造函数来创建对象来强制构造函数的存在。请记住,如果没有在任何地方使用构造函数,则它们不是必需的。

      【讨论】:

      • 我不同意。有时最好提供一些功能,即使你不确定你是否会使用它们,仅仅因为它们的存在是由其他东西暗示的。例如,如果您定义operator&gt;,即使您不打算使用它,您也应该定义operator&lt;。就我而言,我具有加载资源fom文件或从内存中的功能;如果您愿意,也可以直接从构造函数加载资源。
      • @Paul:实际上,您不应该这样做。你应该让standard relational operators 来代替它。 Daniel 是对的——你写的任何东西至少应该被单元测试使用,而且最好不止这些。
      • @Paul:这个问题的问题在于,interface 提出这样的要求是否合理。存在一个接口是为了指定特定上下文如何使用其对象,而不是它们最初的创建方式。
      【解决方案4】:

      为了构造一个对象,您需要知道要使用的具体类(否则它如何知道要分配多少内存,或者要使用哪个虚拟表等?)。因此,在处理构造函数时,接口不起作用,并且您不能使用接口(纯虚拟)来强制存在这样的构造函数。当您考虑它时,这是很自然的,虚拟仅在您拥有多态对象时才起作用,即在实例化之后。任何引用您的 IResource 接口的人都只会处理实例化的对象,而永远不会触及构造函数。

      如果您愿意,您可以使用模板对内容强制执行此类约束。通过简单地从模板化函数调用复制构造函数,如果遇到使用没有复制构造函数的类型的模板实例化,编译器会报错。

      【讨论】:

      • 请注意,通过“调用复制构造函数”,意味着您必须使用 CRTP。
      • @Billy:是的,或者可能涉及更多模板的一些变化。
      猜你喜欢
      • 2017-01-14
      • 2010-09-26
      • 2018-12-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多