【问题标题】:C++, virtual inheritance, strange abstract class + clone problemC++,虚拟继承,奇怪的抽象类+克隆问题
【发布时间】:2011-10-19 03:16:13
【问题描述】:

抱歉,源代码量较大。存在三个抽象类P、L、PL。第三个类 PL 是使用虚拟继承从类 P 和 L 派生的:

template <typename T>  //Abstract class
class P
{

    public:
            virtual ~P() = 0;
    virtual P <T> *clone() const  = 0;
};

template <typename T>
P<T>::~P() {}

template <typename T>
P<T> * P <T>:: clone() const { return new P <T> ( *this ); }

template <typename T>  //Abstract class
class L
{
    public:
            virtual ~L() = 0;
    virtual L <T> *clone() const = 0;
};

template <typename T>
L<T>::~L() {}

template <typename T>
L<T> *L <T> ::clone() const { return new L <T> ( *this );}

template <typename T>
class PL: virtual public P <T>, virtual public L <T>  //Abstract class
{
    public:
            PL() : P <T>(), L<T>() {}
    virtual ~PL() = 0;
    virtual PL <T> *clone() const  = 0;
};

template <typename T>
PL<T>::~PL() {}

template <typename T>
PL<T> * PL <T> :: clone() const { return new PL <T> ( *this );}

每个类都有自己的克隆方法实现。

接下来的两个类 PA、PC 是使用虚拟继承从类 P 派生的:

template <typename T>
class PC : virtual public P <T>
{
    public:
            PC() : P <T> () {}
            virtual ~PC() {}
            virtual PC <T> *clone() const {return new PC <T> ( *this );}
};

template <typename T>
class PA : virtual public P <T>
{
    public:
            PA() : P <T> () {}
            virtual ~PA() {}
            virtual PA <T> *clone() const {return new PA <T> ( *this );}
};

最后两个类 PCL 和 PAL 是使用从 PC 和 PL 、 PA 和 PL 的虚拟继承来驱动的。

template <typename T>
class PCL : public PC <T>, public PL <T>
{
    public:
            PCL() : P <T> (), PC <T> (), PL <T> ()  {}
            virtual ~PCL() {}
            virtual PCL <T> *clone() const {return new PCL <T> ( *this );}
};

template <typename T>
class PAL : public PA <T>, public PL <T>
{
    public:
            PAL() : P <T> (), PA <T> (), PL <T> () {}
            virtual ~PAL() {}
            virtual PAL <T> *clone() const {return new PAL <T> ( *this );}

};

有类依赖关系图:

.......... P .... L.....
........../|\..../......
........./.|.\../.......
......../..|..\/........
.......PC..PA..PL.......
.......|...|.../|.......
.......|...|../.|.......
.......|...PAL..|.......
.......|........|.......
.......PCL_____/........  

请不要讨论这个提议:-)))。我有以下 3 个问题:

1) 这个类依赖是否用 C++ 正确重写(首先是“虚拟”的位置)?

2) 不知道哪里出错了,请看代码:

int main(int argc, _TCHAR* argv[])
{
PCL <double> * pcl = new PCL <double>(); //Object of abstract class not allowed
PAL <double> * pal = new PAL <double>(); //Object of abstract class not allowed
PL <double> *pl1 = pcl->clone(); //Polymorphism
PL <double> *pl2 = pal->clone(); //Polymorphism
return 0;
} 

无法创建 PAL / PCL 类的新对象,这两个类都标记为抽象类。但它们并不抽象。问题出在哪里?

3) 是否可以将多态性与 clone() 方法一起使用?请看上面的代码...

感谢您的帮助...


更新问题

我更正了代码。但是使用VS 2010编译器出现如下错误:

template <typename T>  //Abstract class
class P
{
    public:
    virtual ~P() = 0;
    virtual P <T> *clone() const  = 0;
};

template <typename T>
P<T>::~P() {}

template <typename T>
P<T> * P <T>:: clone() const { return new P <T> ( *this ); }


template <typename T>  //Abstract class
class L
{
    public:
    virtual ~L() = 0;
    virtual L <T> *clone() const = 0;
};

template <typename T>
L<T>::~L() {}

template <typename T>
L<T> *L <T> ::clone() const { return new L <T> ( *this );}


template <typename T>
class PL: virtual public P <T>, virtual public L <T>  //Abstract class
{
    public:
            PL() : P <T>(), L<T>() {}
    virtual ~PL() = 0;
    virtual PL <T> *clone() const  = 0; 
};

template <typename T>
PL<T>::~PL() {}

template <typename T>
PL<T> * PL <T> :: clone() const { return new PL <T> ( *this );}


template <typename T>
class PC : virtual public P <T>
{
    protected:
            T pc;
    public:
            PC() : P <T> () {}
    virtual ~PC() {}
            virtual PC <T> *clone() const {return new PC <T> ( *this );}
};

template <typename T>
class PA : virtual public P <T>
{
    public:
            PA() : P <T> () {}
    virtual ~PA() {}
            virtual PA <T> *clone() const {return new PA <T> ( *this );}
};

template <typename T>
class PCL : public PC <T>, public PL <T>
{
public:
            PCL() : P <T> (), PC <T> (), PL <T> () {}
            virtual ~PCL() {}
            virtual PCL <T> *clone() const {return new PCL <T> ( *this   );}
}; //Error using VS 2010: Error 1   error C2250: 'PCL<T>' : ambiguous inheritance of 'PC<T> *P<T>::clone(void) const'


template <typename T>
class PAL : public PA <T>, public PL <T>
{
public:
            PAL() : P <T> (), PA <T> (), PL <T> ()  {}
            virtual ~PAL() {}
            virtual PAL <T> *clone() const {return new PAL <T> ( *this );}
}; //Error VS 2010: Error   1   error C2250: 'PAL<T>' : ambiguous inheritance of 'PA<T> *P<T>::clone(void) const'


int main(int argc, char* argv[])
{
PCL <double> * pcl = new PCL <double>();
PAL <double> * pal = new PAL <double>();
PL <double> *pl1 = pcl->clone();
PL <double> *pl2 = pal->clone();
return 0;
}

也许我忽略了一些东西...但是 g++ 编译此代码正常。

【问题讨论】:

  • PLPL 中的纯虚拟析构函数有默认实现吗?
  • 我认为你不能在纯函数声明中添加定义;说= 0;。在PL &lt;T&gt; { } 之后也缺少括号(应该是PL &lt;T&gt;() { })。正如@Chad 所说,如果您已经拥有其他纯虚函数,则无需将析构函数设为纯虚函数。
  • @Chad:是的,我更正了代码...

标签: c++ inheritance virtual abstract-class clone


【解决方案1】:

只是代码中的一些小错误:

  • 正确设置基类初始化器:PAL() : PC &lt;T&gt;(), PL &lt;T&gt;() {} P&lt;T&gt; 没有初始化,这不是直接基类;但是[抱歉,那是错误的——你确实必须调用虚拟基础构造函数,因为虚拟继承。]你必须说圆括号。

  • 声明没有定义的纯虚函数:virtual L &lt;T&gt; *clone() const = 0;

  • 再次检查您是否希望 PL 虚拟继承 P 但非虚拟继承 L(但这很好)。

  • 仔细检查所有clone()s 是否具有相同的常量。

  • 如果您的抽象基类中已经有一个纯虚函数,则不应将析构函数设为纯。相反,比如说virtual ~T() { }。否则,您仍应提供纯虚拟析构函数的定义(因为析构函数始终需要可调用):virtual T::~T() { }

【讨论】:

  • 确实,纯错误必须来自 clone() 不是 const。 ideone.com/3bTty 是代码的清理版本,可以编译(虽然不是链接)。由于代码有这么多错误,是否真的对应OP遇到问题的代码值得怀疑。
  • 我实际上没有收到任何关于 constness 的警告或错误。我现在不是 100% 确定,但有可能发生错误的 hiding 而不是覆盖。我现在不想花更多时间在这上面,但是在覆盖时正确获取签名非常重要。
  • 我也不希望有任何关于 constness 的警告或错误,它只是另一个签名,只是将其视为函数名称中的拼写错误。
  • 那么,有什么问题呢?我编译好了,还有其他问题吗?
  • 我收到以下错误:错误 1 ​​错误 C2250: 'PCL' : 'PC *P::clone(void) const' 的模糊继承.. . 查看更新的代码,请... ;
【解决方案2】:

因为你的 P 类中的 clone 方法在定义时是抽象的

virtual P <T> *clone() const = 0 {};

(顺便说一句,{} 不正确)。您必须了解的是,一旦实例化此模板,这是一个单独的方法,其签名与派生类克隆方法完全不同。模板实例化创建的行为就像它生成新代码一样。因此,这是一个从未实现过的纯抽象方法。从而使继承此方法的每个人(每个派生类)都成为抽象类。


编辑:至于第三个问题。运行时和编译时多态性不能很好地混合。我不知道你到底为什么要使用如此复杂的结构,但我相信设计可以大大简化。

【讨论】:

  • 因此这是一个从未实现过的纯抽象方法不太正确,因为这些函数不是成员模板,它们的返回类型仅取决于模板参数类模板,这很好。派生类实现的协变返回也可以,尽管很难发现(这是人们应该重新考虑该设计的众多原因之一)
【解决方案3】:
  1. 您的代码中有几个拼写错误需要修复,但是 继承格中的virtuals 是正确的。 (他们是 最低限度。您可以添加更多;在这种格子的极少数情况下 发生时,通常最好将所有继承设为虚拟,以保护 反对未来的演变。)

  2. 您的测试代码没有问题。一旦出现错别字 原版是固定的,它可以用 g++ 编译。

  3. 这是可能的,它应该按照您的方式工作。

【讨论】:

    【解决方案4】:

    MSVC 不正确支持协变返回。你认为你已经覆盖了函数的地方,实际上你只是隐藏了它。这就是手头的问题。

    【讨论】:

    • @Johnas:你能删除PL &lt;T&gt; *PL &lt;T&gt;::clone()吗?因为那时它为我编译。 (也就是说,当我删除试图实例化抽象类的纯虚拟 clone() 函数的实现时。)
    • 您能否给我一个建议,如何修改代码以使用 MSVS 2010 进行翻译?
    • @Johnas:正如我所说,如果我删除 PL &lt;T&gt; *PL &lt;T&gt;::clone(),它将为我编译。
    • 我和你一样在同一时间发送了我的帖子 :-) 删除 PL * PL :: clone() const { return new PL ( *this ); } 没有任何改变,同样的错误......
    • @Johnas:不要只删除成员函数的定义,还要删除它的声明。这是为我编译的。
    【解决方案5】:

    这是使用 VC10 为我编译的:

    template <typename T>  //Abstract class
    class P
    {
    public:
        virtual ~P() = 0;
        virtual P <T> *clone() const  = 0;
    };
    
    template <typename T>
    P<T>::~P() {}
    
    
    template <typename T>  //Abstract class
    class L
    {
    public:
        virtual ~L() = 0;
        virtual L <T> *clone() const = 0;
    };
    
    template <typename T>
    L<T>::~L() {}
    
    
    template <typename T>
    class PL: virtual public P <T>, virtual public L <T>  //Abstract class
    {
    public:
        PL() : P <T>(), L<T>() {}
        virtual ~PL() = 0;
    //  virtual PL <T> *clone() const  = 0; // REMOVED!
    };
    
    template <typename T>
    PL<T>::~PL() {}
    
    
    template <typename T>
    class PC : virtual public P <T>
    {
    protected:
        T pc;
    public:
        PC() : P <T> () {}
        virtual ~PC() {}
        virtual PC <T> *clone() const {return new PC <T> ( *this );}
    };
    
    template <typename T>
    class PA : virtual public P <T>
    {
    public:
        PA() : P <T> () {}
        virtual ~PA() {}
        virtual PA <T> *clone() const {return new PA <T> ( *this );}
    };
    
    template <typename T>
    class PCL : public PC <T>, public PL <T>
    {
    public:
        PCL() : P <T> (), PC <T> (), PL <T> () {}
        virtual ~PCL() {}
        virtual PCL <T> *clone() const {return new PCL <T> ( *this   );}
    };
    
    
    template <typename T>
    class PAL : public PA <T>, public PL <T>
    {
    public:
        PAL() : P <T> (), PA <T> (), PL <T> ()  {}
        virtual ~PAL() {}
        virtual PAL <T> *clone() const {return new PAL <T> ( *this );}
    };
    
    
    int main()
    {
        PCL <double> * pcl = new PCL <double>();
        PAL <double> * pal = new PAL <double>();
        PL <double> *pl1 = pcl->clone();
        PL <double> *pl2 = pal->clone();
        return 0;
    }
    

    请注意,我必须删除 PL &lt;T&gt; *PL &lt;T&gt;::clone() 才能让 VC 接受代码。问题在于,如果你有一个PL&lt;T&gt;,并称它为clone(),它将静态返回L&lt;T&gt;*,而不是PL&lt;T&gt;*,即使动态类型是从@ 派生的类987654327@。

    【讨论】:

    • 好的,它有效。但是您删除了三个抽象类中所有克隆方法的定义。
    • @Johnas:这些方法试图实例化它们自己的对象,abstract 类。那肯定是行不通的。但无论如何你都不需要它们,因为你永远不会拥有这些类的对象(因为它们是抽象的)。您将永远只有派生类的对象,并且这些对象确实实现了clone()。真的,我的答案中的代码的唯一问题是我在代码下面的答案中写的。
    • 我忘记了,我可以创建指向抽象类的指针,但不能创建对象(也不是动态的)...感谢您的帮助...
    猜你喜欢
    • 2011-03-13
    • 2015-03-24
    • 2014-08-16
    • 1970-01-01
    • 2023-03-14
    • 2012-10-11
    • 2019-12-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多