Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/124.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 组合与继承设计问题_C++_Oop_Design Patterns - Fatal编程技术网

C++ 组合与继承设计问题

C++ 组合与继承设计问题,c++,oop,design-patterns,C++,Oop,Design Patterns,我从一个实现了抽象类形式的算法的库中获得了这些虚拟类 class A{ public : virtual void foo() = 0 ; }; class B{ public: void setA(A * a) { m_a = a ;} virtual void f() = 0; void g() {m_a->foo();} protected: A * m_a ; }; 要使用库

我从一个实现了抽象类形式的算法的库中获得了这些虚拟类

class A{
    public : 
    virtual void foo() = 0 ;
};


class B{
    public:
        void setA(A * a) { m_a = a ;}

        virtual void f() = 0;
        void g() {m_a->foo();}
    protected:
        A * m_a ;
 };
要使用库,您只需派生类并实现纯虚拟函数,如
foo()
,并提供特定于实现的其他方法(
bar()

我通常会通过调用

dB * mydB = new dB() ;
mydB->setA(new dA() );
mydB->f() ;
mydB->g() ;
但是,在实现
dB::f()
时,我遇到了一个设计问题,因为我需要调用
dA::bar()
,它特定于
dB
。但在课堂上,我只通过AB*保留对dB的引用。然后我想到了两个选择:

  • 每次调用
    f()
    时,使用
    dynamic\u cast
    B::m\u a
    转换为
    dB*
  • 向dB添加一个m_dA成员,该成员存储与m_a相同的指针,但可用于访问特定于dB的函数
当然,我不能更改基类


我想知道这个问题是否有更优雅的解决方案(比如我没有想到的设计模式)。如果没有,我应该选择哪一个?

您有第三个解决方案。在
dB
中添加函数
setA()。当然,此函数将隐藏
B::setA()
,如果将
dB::setA()
实现为:

class dB : public B 
{
     dA *m_dA ; //add this member also!
     public :
          void f()
          {
             m_dA->bar(); //fast forward : no cast here!
          }
          void setA(A *a) //this hides B::setA()
          {
             m_dA= dynamic_cast<dA*>(a); //just one time dynamic cast!
             if ( m_dA == 0 )
             {  
                 throw std::runtime_error("invalid argument");
             }
             B::setA(a); //now call the hidden function in the base!
          }
};
dB类:公共B类
{
dA*m_dA;//也添加此成员!
公众:
void f()
{
m_dA->bar();//快进:这里没有演员!
}
void setA(A*A)//这将隐藏B::setA()
{
m_dA=dynamic_cast(a);//只需一次dynamic cast!
如果(m_dA==0)
{  
抛出std::runtime_错误(“无效参数”);
}
setA(a);//现在调用基中的隐藏函数!
}
};

这样,您就不需要在每次调用
dB::f()
时进行动态\u强制转换,这使得调用很快

您还有第三种解决方案。在
dB
中添加函数
setA()。当然,此函数将隐藏
B::setA()
,如果将
dB::setA()
实现为:

class dB : public B 
{
     dA *m_dA ; //add this member also!
     public :
          void f()
          {
             m_dA->bar(); //fast forward : no cast here!
          }
          void setA(A *a) //this hides B::setA()
          {
             m_dA= dynamic_cast<dA*>(a); //just one time dynamic cast!
             if ( m_dA == 0 )
             {  
                 throw std::runtime_error("invalid argument");
             }
             B::setA(a); //now call the hidden function in the base!
          }
};
dB类:公共B类
{
dA*m_dA;//也添加此成员!
公众:
void f()
{
m_dA->bar();//快进:这里没有演员!
}
void setA(A*A)//这将隐藏B::setA()
{
m_dA=dynamic_cast(a);//只需一次dynamic cast!
如果(m_dA==0)
{  
抛出std::runtime_错误(“无效参数”);
}
setA(a);//现在调用基中的隐藏函数!
}
};

这样,您就不需要在每次调用
dB::f()
时进行动态\u强制转换,这使得调用很快

只有当数据库的A实际上是dA时,它才能工作。你需要确保情况始终如此


因此,超越setA方法,使用动态_转换来检查它是否真的是dA。现在,无论你在那一点上保存的结果在m_dA或动态铸造后再次是不重要的。不太可能出现初始化错误的dB,其动态转换稍后可能会失败。

只有当dB的A实际上是dA时,它才能工作。你需要确保情况始终如此


因此,超越setA方法,使用动态_转换来检查它是否真的是dA。现在,无论你在那一点上保存的结果在m_dA或动态铸造后再次是不重要的。不太可能出现初始化错误的dB,其动态强制转换稍后可能会失败。

dB
作为一个具体类,不应该在不相关的具体类上调用方法。即使使用
reinterpret_cast
这也是一种糟糕的设计,它不必要地耦合了不相关的对象。公共功能应该放在公共基类或接口中

例如,类
A
可以被视为一个接口,因为它只有纯虚拟方法。因此,如果您还希望在
dB
上使用该接口,那么使用多重继承是安全的。在这种情况下,您当然必须实现自己的
foo

class dB : public B, public A {
     public :
          void f();
          void foo();
};
如果
bar()
是您想要的,并且出于某种原因,您可以更改接口
A
,然后创建您自己的接口,该接口提供纯虚拟函数
bar()
,然后使
dA
dB
从新接口继承并实现
bar()
并相应地使用接口指针


如果您必须在另一个方面使用其中一个,那么组合是一种方法,但不能使用可能导致动态强制转换失败的模棱两可的基指针。可能在
dB
中创建一个具体的
dA
成员。

dB
作为一个具体类,不应该调用与
dA
无关的具体类上的方法。即使使用
reinterpret_cast
这也是一种糟糕的设计,它不必要地耦合了不相关的对象。公共功能应该放在公共基类或接口中

例如,类
A
可以被视为一个接口,因为它只有纯虚拟方法。因此,如果您还希望在
dB
上使用该接口,那么使用多重继承是安全的。在这种情况下,您当然必须实现自己的
foo

class dB : public B, public A {
     public :
          void f();
          void foo();
};
如果
bar()
是您想要的,并且出于某种原因,您可以更改接口
A
,然后创建您自己的接口,该接口提供纯虚拟函数
bar()
,然后使
dA
dB
从新接口继承并实现
bar()
并相应地使用接口指针

如果你必须用一个来表示另一个,那么合成是一条路要走,但不能用模棱两可的基指针