【问题标题】:Declaring pure virtual function in base class with derived class object as arguments在基类中声明纯虚函数,派生类对象作为参数
【发布时间】:2013-12-22 21:08:22
【问题描述】:

如果这个话题之前已经提出并解决过,请提前道歉。

我想创建一个带有纯虚函数(抽象类)的基类,这样当从它创建派生类时,用户有义务在派生类中实现这个函数的主体。关键是在纯虚函数中,我想将派生类型的参数对象作为参数。这可能吗?

例如:

class Base{

 public:
  virtual void DoSomething (A object1, B object2) = 0;
};

class A: public Base{

 public:
  DoSomething(A x, B y) {

   ...
   ...
};

};

class B: public Base{

 public:
  DoSomething(A x, B y) {

   ...
   ...
};

};

我试过了,但是编译器抱怨说它对派生类一无所知,我理解这是因为它们是在基类之后创建的。有没有办法规避?还是有另一种方法可以做我想做的事,即强制用户在派生类中使用派生类类型的 arguments 参数对象实现函数体?

提前致谢!

【问题讨论】:

  • 听起来你需要模板...
  • 您可能想了解Curiously recurring template pattern。它本身可能无法帮助您解决这个问题,但它可能会给您一些想法。
  • 以上任何一种,或者将参数作为引用/指针,然后您可以转发声明的类。
  • 我发誓我在阅读了上面链接到你的文章之后写了下面的回复,诚实。尽管这个例子几乎一字不差...

标签: c++ arguments derived-class base-class pure-virtual


【解决方案1】:

使用引用和前向声明:

// Forward declaration
class A;
class B;

class Base
{
public:
    virtual void DoSomething(const &A a, const B& b) = 0;
};

class A: public Base
{
public:
    virtual void DoSomething(const &A a, const B& b) /* override */;
};

【讨论】:

    【解决方案2】:

    编辑:最终版本使用 const 引用 :-)

    ///Test-code. place anywhere in global C++ scope for testing
    class A;
    class B;
    
    class Base{
    
     public:
      virtual void DoSomething ( const A & object1, const B & object2) = 0;
    };
    
    class A: public Base{
    
     public:
      void DoSomething(const A & object1, const B & object2) { 
        int i=0;
        i++; //test break point
      };
    
    };
    
    class B: public Base{
    
     public:
      void DoSomething(const A & object1, const B & object2) { 
        int i=0;
        i++; //test break point
      };
    
    };
    
    bool test()
    {
        A a;
        B b;
    
        a.DoSomething( A(), B() );
        b.DoSomething( A(), B() );
        return true;
    }
    
    static bool invokeTest = test();
    ///END of test-code
    

    编辑:使用 auto_ptr 的更好版本

    ///Test-code. place anywhere in global C++ scope for testing
    #include <memory>
    using std::auto_ptr;
    class A;
    class B;
    
    
    
    
    class Base{
    
     public:
      virtual void DoSomething (auto_ptr<A> object1, auto_ptr<B> object2) = 0;
    };
    
    class A: public Base{
    
     public:
      void DoSomething(auto_ptr<A> x, auto_ptr<B> y) { 
        int i=0;
        i++; //test break point
      };
    
    };
    
    class B: public Base{
    
     public:
      void DoSomething(auto_ptr<A> x, auto_ptr<B> y) { 
        int i=0;
        i++; //test break point
      };
    
    };
    
    bool test()
    {
        Base *a = new A;
        Base *b = new B;
    
        a->DoSomething( auto_ptr<A>(new A), auto_ptr<B>(new B) );
        b->DoSomething( auto_ptr<A>(new A), auto_ptr<B>(new B) );
        return true;
    }
    
    static bool invokeTest = test();
    ///END of test-code
    

    原答案

    如果参数是指针,那么它应该可以工作:

    class A;
    class B;
    
    class Base{
    
     public:
      virtual void DoSomething (A * object1, B * object2) = 0;
    };
    
    class A: public Base{
    
     public:
      void DoSomething(A *x, B *y) {
    
    };
    
    };
    
    class B: public Base{
    
     public:
      void DoSomething(A *x, B *y) {
    
    };
    
    };
    

    P.S.:我想知道你为什么需要它?

    编辑 P.P.S.:我仍然想知道为什么需要它:-)?

    【讨论】:

    • 我会改用 (const) 引用(因为他按值传递对象,所以他没有获得对象的所有权)。 auto_ptr 在 C++11 中已被弃用。
    • @Jarod42:真的!我已将示例更改为使用 const refs。我也工作。感谢您提到 auto_ptr 已被弃用。真可惜,我还没有使用 C++11 :-(
    【解决方案3】:

    是的,但您需要模板。

    class AbstractBase
    {
    }
    
    template <typename D>
    class Base : public AbstractBase
    {
    public:
        virtual D* func() = 0;
    };
    
    class Derived : public Base<Derived>
    {
    public:
        Derived* func() { return this; }
    };
    

    您最终会得到每个派生类型的 Base 模板实例化。这意味着如果您有class Derived1 : public Base&lt;Derived1&gt;class Derived2 : public Base&lt;Derived2&gt;,那么它们确实共享同一个Base 类;但由于Base&lt;T&gt; 继承自非模板化AbstractBase,因此它们都继承自那个。 (当然,您可以以正常方式从 Derived 继承。)

    【讨论】:

    • 很想知道你将如何为 2 个子类和无指针编写它。
    • class Derived1 : public Base&lt;Derived1&gt;class Derived2 : public Base&lt;Derived2&gt;。这意味着,正如我所说,Derived1Derived2 不在同一个类层次结构中 --- 它们具有不同的超类(分别为 Base&lt;Derived1&gt;Base&lt;Derived2&gt;)。拥有一个模板化的Base 继承自的AbstractBase 可能是明智的。
    • 不,你已经用一个子类做了两次 :) 但是 OP 想要在 Base 中使用 void DoSomething(Derived1, Derived2)。 CRTP 不适合那个,至少不适合大量按摩......
    • ...哦,是的,不,这行不通。但为了清楚起见,我还是更新了我的答案。
    【解决方案4】:

    非常感谢您的及时答复!使用 const ref 的解决方案效果很好! 有人问为什么需要这样的东西。我会尽快解释。我是物理学家,我正在尝试实现对象之间的一些交互。

    Base 类应该包含对象的一些通用内容。派生类 (A, B, ...) 应该表示具有不同属性的对象。然后,我尝试实现每个对象类型(派生类)的物理特性及其与其他对象(其他派生类)的交互。 A 可以与 B 或 C 反应,B 可以与 A、C 或 D 反应,等等...

    由于每个对象都有自己的物理特性并且每次交互都可能不同,因此无法在 Base 类中实现 DoSomething 函数的主体。因此,我在 Base 类中使用了纯虚函数。

    例如,A 可以与 B 类或 C 类的对象交互。那么,我应该在 Base 类中 虚拟 void DoSomething(const A & object1, const B & object1) = 0; virtual void DoSomething(const A & object1, const C & object1) = 0;

    然后我在派生类 A 中重写该函数两次。对于其他派生类,依此类推。

    你认为它可以用其他更聪明的方式来完成吗?

    谢谢!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-01-25
      • 1970-01-01
      • 1970-01-01
      • 2014-01-01
      • 1970-01-01
      • 2018-08-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多