C++专用多态实现设计

C++专用多态实现设计,c++,c++11,polymorphism,C++,C++11,Polymorphism,这是一个C++11问题。 我有一个对象objmyobj,它封装了一个MyType类型的对象f。 根据运行时上下文,对象F的行为应该有所不同 实现这一点的一种自然方式是类Obj封装指向抽象基类MyType的指针,根据上下文指向MyType的不同公共子类,如MyType1、MyType2等 然而,我不太喜欢Obj因为MyType是多态的而遭受后果,即必须处理指针。特别是,如果我将其设置为std::unique_ptr,这意味着Obj要么不能被复制,要么需要为其提供一个适当的复制构造函数,以处理复制M

这是一个C++11问题。 我有一个对象objmyobj,它封装了一个MyType类型的对象f。 根据运行时上下文,对象F的行为应该有所不同

实现这一点的一种自然方式是类Obj封装指向抽象基类MyType的指针,根据上下文指向MyType的不同公共子类,如MyType1、MyType2等

然而,我不太喜欢Obj因为MyType是多态的而遭受后果,即必须处理指针。特别是,如果我将其设置为std::unique_ptr,这意味着Obj要么不能被复制,要么需要为其提供一个适当的复制构造函数,以处理复制MyType资源的问题

在我看来,MyType多态性不应该是Obj的问题

我参加了以下课程。基本上,这个想法是将指针隐藏在MyTypeprivate属性中。此外,我的第二个问题涉及MyTypeImpl的具体实现可能共享一些代码,因此不应重复。我把它放在了一个类中,具体的实现是从这个类私有继承的

我很好奇比我更专业的开发者会怎么想。它是不是太重了以至于无法隐藏指针?有更好的方法吗

#include <iostream>
#include <memory>


// a "standard" implementation of MyType
class MyTypeImpl
{
    public:        
        virtual double operator()(double a) = 0;
        virtual int implType() const = 0;
        virtual void complexStuff() const = 0;
};



// some internal stuff common to all implementations
class MyTypeImplInternals
{
protected:    
    MyTypeImplInternals(int value):factor_{value}{}
    int factor_;
    void longCommonFunction() const{ std::cout << "I'm doing complex stuff common to all interfaces " << factor_ << "\n" ;}
};


// one specific implementation
class MyTypeImpl1: public MyTypeImpl, private MyTypeImplInternals
{
    public:
        MyTypeImpl1(int factor):MyTypeImplInternals{factor}{};

        virtual double operator()(double a) override {return factor_*a;} 
        virtual int implType() const override {return 1;}
        virtual void complexStuff() const override { longCommonFunction(); }
};


// a second implementation
class MyTypeImpl2: public MyTypeImpl, private MyTypeImplInternals
{
    public:
        MyTypeImpl2(int factor):MyTypeImplInternals{factor}{};
        virtual double operator()(double a) override {return factor_*a;} 
        virtual int implType() const override {return 2;}
        virtual void complexStuff() const override { longCommonFunction(); }
};



class MyTypeImplFactory
{
    public:
    static std::unique_ptr<MyTypeImpl>createMyTypeImpl(int implementationType)
    {        
        switch(implementationType)
        {
            case 1:            
              return std::unique_ptr<MyTypeImpl> (new MyTypeImpl1(12));

             case 2:
                return std::unique_ptr<MyTypeImpl> (new MyTypeImpl2(22));

            default:
                throw std::runtime_error("implementation does not exist...\n");
                return nullptr;
        }
    }
};



// my type
class MyType
{
    public:

        MyType(int implementationType)
        {
            implPtr_ = MyTypeImplFactory::createMyTypeImpl(implementationType);   
        }

        MyType(MyType const& source)
        : implPtr_{ MyTypeImplFactory::createMyTypeImpl(source.implType()) }
        {
        }


        double operator()(double a){return (*implPtr_)(a);}  
        int implType() const {return implPtr_->implType();}
        void complexStuff() const {implPtr_->complexStuff();}


    private:
        std::unique_ptr<MyTypeImpl> implPtr_;
};








class Obj
{
 private:
    MyType f;

public:
    Obj(int dim):f{dim}{}    
    Obj(Obj&& sourceToMove) = default;
    Obj(Obj const& source) = default;

    void doStuff() {std::cout << "I'm doing stuff()  " << f(2) << std::endl; f.complexStuff();}
};







int main()
{
 Obj myObj{1}, myObj2{2};

 myObj.doStuff();
 myObj2.doStuff();

 Obj myObj3{std::move(myObj2)}; // myObj2 now dead
 Obj myObj4{myObj}; 

 myObj3.doStuff();
 myObj4.doStuff();

}
链接到联机编译器:

这里的实现就是一个非常愚蠢的例子。该设计的一个应用可能是一个解算器Obj,该解算器可以解某种物理方程MyType,其精确定义取决于问题的维度,一维空间中的方程与二维或三维中的方程不同。解算器的代码将完全独立于方程的维数,也不必处理指针。方程将对外界隐藏其1D、2D或3D实现


最初是一篇被搁置的帖子,因为抽象地说,这个提议的类设计似乎有一个明显的问题。多态类型由std::unique\u ptr引用:

Obj的默认复制构造函数和赋值运算符将最终将保留的指针转移到新对象,使原始对象中的std::unique_ptr盯着null ptr。不太好

至少这应该是一个std::shared_ptr,或者Obj的复制构造函数和赋值操作符需要实例化一个新的impltr_u。请注意,使用easy std::shared_ptr修复复制构造函数和赋值运算符的结果时,有多个Obj实例引用同一个MyTypeImpl实例,这可能是问题,也可能不是问题


一个简单得多的类设计就是让MyTypeImpl1和MyTypeImpl2成为Obj的子类,实现所需的多态行为。

我刚刚重构了您的代码

#include <iostream>
#include <memory>


// !abstraction
class MyType
{
    public:        
        virtual double operator()(double a) = 0;
        virtual int implType() const = 0;
        virtual void complexStuff() const = 0;
};

// !!MyTypeImplInternals could be a super class of MyTypeImpl* if it has properties(such as factor_) or just some static functions.
class MyTypeImplInternals
{
public:    
    MyTypeImplInternals(int value):factor_{value}{}
    int factor_;
    void longCommonFunction() const{ std::cout << "I'm doing complex stuff common to all interfaces " << factor_ << "\n" ;}
};

// one specific implementation
class MyTypeImpl1: public MyType
{
    MyTypeImplInternals internal_;
    public:
        MyTypeImpl1(int factor):internal_{factor}{};

        virtual double operator()(double a) override {return internal_.factor_*a;} 
        virtual int implType() const override {return 1;}
        virtual void complexStuff() const override { internal_.longCommonFunction(); }
};

// a second implementation
class MyTypeImpl2: public MyType
{
    MyTypeImplInternals internal_;
    public:
        MyTypeImpl2(int factor):internal_{factor}{};
        virtual double operator()(double a) override {return internal_.factor_*a;} 
        virtual int implType() const override {return 2;}
        virtual void complexStuff() const override { internal_.longCommonFunction(); }
};

std::unique_ptr<MyType> createMyType(int implementationType)
{        
    switch(implementationType)
    {
        case 1:            
          return std::unique_ptr<MyType> (new MyTypeImpl1(12));

         case 2:
            return std::unique_ptr<MyType> (new MyTypeImpl2(22));

        default:
            throw std::runtime_error("implementation does not exist...\n");
            return nullptr;
    }
}

class Obj
{
private:
    std::unique_ptr<MyType> f_;

public:
    Obj(int dim):f_(createMyType(dim)){}    
    Obj(Obj&& sourceToMove) : f_(std::move(sourceToMove.f_)) {}
    Obj(Obj const& source) : f_(createMyType(source.f_->implType())) {}

    void doStuff() {std::cout << "I'm doing stuff()  " << (*f_)(2) << std::endl; f_->complexStuff();}
};

int main()
{
 Obj myObj{1}, myObj2{2};

 myObj.doStuff();
 myObj2.doStuff();

 Obj myObj3{std::move(myObj2)}; // myObj2 now dead
 Obj myObj4{myObj};  //!!Bad idea to share an implementation to more Objs.

 myObj3.doStuff();
 myObj4.doStuff();

}

这实际上是两个不同的问题,因此应该是两个独立的SO问题,每个问题都有简化的示例代码,以便更容易提取问题的本质。目前,这是一大堆代码。Obj真的需要可复制吗?MyType可以使用标准类型擦除技术。作为标准库的一个例子,考虑STD::函数:它是可复制的和有价值的,但是管理运行时确定的调度实现。在Internet中搜索,我发现我的代码看起来像piml模式。唯一的区别是这里的Pimpl指针是多态的。这真的是太多的代码吗?用多态复制语义实现您的owm智能指针。我不知道为什么Obj复制构造函数会导致问题。相反,在我看来,Obj default copy ctor将调用memberwise copy ctor,即调用MyType copy COSTRUCTOR,我将其定义为使用工厂分配新的ImplPtr_。也许您应该查看std::unique_ptr的复制构造函数的功能。我知道unique_ptr只有move ctor,不能复制。移动它时,它会转移所有权,并设置为nullptr OK。但在本例中,Obj copy ctor调用我定义的MyType copy构造函数,我认为原始的unique_ptr从未移动过。不我已经检查了在线代码,查看源指针和新指针所持有的地址,它们都不是空的,并且是不同的。。。您认为这里哪里不对?MyTypeMyType const&source:implPtr{MyTypeImplFactory::createMyTypeImplsource.implType}{}您做了哪些具体更改,为什么?这种重构如何比原始重构更好?就目前而言,这是一个非答案。您基本上已经在MyType中去掉了指向抽象实现的私有指针,并在Obj中放入了一个唯一的_ptr。这是我在帖子开头所说的自然方式。我更喜欢Obj的代码不必处理指针,因为MyType是多态的MyType的复制构造函数只是创建了一个具有相同类型的MyTypeImpl。您实施复制是一种困惑
构造函数,但不复制任何内容。我认为如果Obj不是像C中的struct那样的纯数据类,就不应该复制它。真正的问题是Obj的错误用法,而是多态性。。。如果MyType中有其他属性,我会复制它们,但在这个简化的示例中,只有unique_ptr,它不能被复制。。。所以我必须从工厂里创造另一个。。。您会注意到,我不是凭空创建的,而是使用sourceToMove保存的信息来创建的。从Obj的角度来看,MyType只是一个可以复制的类型,为什么不允许复制Obj呢?从语法上讲,对象中的每个属性都是可复制的,除非您不允许复制。没有上下文很难做出决定。考虑到您的代码,我认为关键是要编写更少的代码,以使Obj可复制。所以,一个复制构造函数就足够了。更多的抽象是没有意义的。
#include <iostream>
#include <memory>


// !abstraction
class MyType
{
    public:        
        virtual double operator()(double a) = 0;
        virtual int implType() const = 0;
        virtual void complexStuff() const = 0;
};

// !!MyTypeImplInternals could be a super class of MyTypeImpl* if it has properties(such as factor_) or just some static functions.
class MyTypeImplInternals
{
public:    
    MyTypeImplInternals(int value):factor_{value}{}
    int factor_;
    void longCommonFunction() const{ std::cout << "I'm doing complex stuff common to all interfaces " << factor_ << "\n" ;}
};

// one specific implementation
class MyTypeImpl1: public MyType
{
    MyTypeImplInternals internal_;
    public:
        MyTypeImpl1(int factor):internal_{factor}{};

        virtual double operator()(double a) override {return internal_.factor_*a;} 
        virtual int implType() const override {return 1;}
        virtual void complexStuff() const override { internal_.longCommonFunction(); }
};

// a second implementation
class MyTypeImpl2: public MyType
{
    MyTypeImplInternals internal_;
    public:
        MyTypeImpl2(int factor):internal_{factor}{};
        virtual double operator()(double a) override {return internal_.factor_*a;} 
        virtual int implType() const override {return 2;}
        virtual void complexStuff() const override { internal_.longCommonFunction(); }
};

std::unique_ptr<MyType> createMyType(int implementationType)
{        
    switch(implementationType)
    {
        case 1:            
          return std::unique_ptr<MyType> (new MyTypeImpl1(12));

         case 2:
            return std::unique_ptr<MyType> (new MyTypeImpl2(22));

        default:
            throw std::runtime_error("implementation does not exist...\n");
            return nullptr;
    }
}

class Obj
{
private:
    std::unique_ptr<MyType> f_;

public:
    Obj(int dim):f_(createMyType(dim)){}    
    Obj(Obj&& sourceToMove) : f_(std::move(sourceToMove.f_)) {}
    Obj(Obj const& source) : f_(createMyType(source.f_->implType())) {}

    void doStuff() {std::cout << "I'm doing stuff()  " << (*f_)(2) << std::endl; f_->complexStuff();}
};

int main()
{
 Obj myObj{1}, myObj2{2};

 myObj.doStuff();
 myObj2.doStuff();

 Obj myObj3{std::move(myObj2)}; // myObj2 now dead
 Obj myObj4{myObj};  //!!Bad idea to share an implementation to more Objs.

 myObj3.doStuff();
 myObj4.doStuff();

}