Объявление чистой виртуальной функции в базовом классе с объектом производного класса в качестве аргументов

Заранее извиняюсь, если эта тема уже поднималась и решалась.

Я хотел бы создать базовый класс с чистой виртуальной функцией (абстрактный класс), чтобы при создании производного класса от него пользователь был обязан реализовать тело этой функции в производном классе. Дело в том, что в чисто виртуальной функции хотелось бы иметь в качестве аргументов объекты производного типа. Это возможно ?

Например:

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 object типа производного класса?

Заранее спасибо !


person Christophe J. Ortiz    schedule 22.12.2013    source источник
comment
Похоже, вам нужны шаблоны...   -  person Oliver Charlesworth    schedule 23.12.2013
comment
Вы можете прочитать о странно повторяющемся шаблоне. Само по себе это может не помочь вам решить эту проблему, но может дать вам идеи.   -  person Some programmer dude    schedule 23.12.2013
comment
Либо выше, либо аргументы в качестве ссылок/указателей, так как тогда вы можете предварительно объявить классы для объявления.   -  person Some programmer dude    schedule 23.12.2013
comment
Клянусь, я написал ответ ниже после прочтения статьи о вас, на которую вы ссылаетесь выше, честно. Несмотря на то, что пример почти дословно идентичен...   -  person David Given    schedule 23.12.2013


Ответы (4)


EDIT: окончательная версия с постоянными ссылками :-)

///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

EDIT: лучшая версия с использованием 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.: Интересно, зачем вам это нужно?

EDIT P.P.S.: Мне все еще интересно, зачем это нужно :-)?

person Valentin Heinitz    schedule 22.12.2013
comment
Вместо этого я бы использовал ссылку (const) (поскольку он передал объект по значению, он не становится владельцем объекта). И auto_ptr устарел в С++ 11. - person Jarod42; 23.12.2013
comment
@Jarod42: правда! Я изменил пример, чтобы использовать константные ссылки. я так же работаю. Спасибо, что упомянули, что auto_ptr устарел. Какой позор, я до сих пор не использую С++ 11 :-( - person Valentin Heinitz; 23.12.2013

Используйте ссылку и предварительное объявление:

// 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 */;
};
person Jarod42    schedule 22.12.2013

Да, но вам нужны шаблоны.

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<Derived1> и class Derived2 : public Base<Derived2>, то они не используют один и тот же класс Base; но поскольку Base<T> наследуется от нешаблонного AbstractBase, они оба наследуют от него. (И, конечно же, вы можете наследовать от Derived обычным способом.)

person David Given    schedule 22.12.2013
comment
Любопытно посмотреть, как бы вы написали это для двух дочерних классов и без указателя. - person Johan; 23.12.2013
comment
class Derived1 : public Base<Derived1> и class Derived2 : public Base<Derived2>. Что означает, как я уже сказал, Derived1 и Derived2 не находятся в одной и той же иерархии классов --- у них разные суперклассы (Base<Derived1> и Base<Derived2> соответственно). Вероятно, разумно иметь AbstractBase, от которого наследуется шаблонный Base. - person David Given; 23.12.2013
comment
Нет, вы сделали это дважды с одним дочерним классом :) Но OP хочет void DoSomething(Derived1, Derived2) в базе. CRTP для этого не подходит, по крайней мере, с большим количеством массажа... - person Johan; 23.12.2013
comment
... о, да, нет, это не работает. Но я все равно обновил свой ответ для ясности. - person David Given; 23.12.2013

Большое спасибо за оперативный ответ! Решение с const ref работает хорошо! Кто-то спрашивал, зачем нужно что-то подобное. Я попытаюсь объяснить вкратце. Я физик, и я пытаюсь реализовать какое-то взаимодействие между объектами.

Базовый класс должен содержать что-то общее с объектами. Производный класс (A, B,...) должен представлять объекты с разными свойствами. Затем я пытаюсь реализовать физику каждого типа объекта (производный класс) и его взаимодействие с другими объектами (другой производный класс). А может реагировать с В или С, В с А, С или D и т. д.

Поскольку у каждого объекта своя физика и каждое взаимодействие может быть разным, тело функции DoSomething нельзя реализовать в классе Base. Поэтому я использую чисто виртуальную функцию в классе Base.

Например, A может взаимодействовать с объектами класса B или класса C. Тогда я должен иметь в базовом классе виртуальную пустоту DoSomething(const A & object1, const B & object1) = 0; виртуальная пустота DoSomething(const A & object1, const C & object1) = 0;

а затем я дважды переопределяю функцию в производном классе A. И так далее для других производных классов.

Как вы думаете, можно ли это сделать каким-то другим и более разумным способом?

Спасибо !

person Christophe J. Ortiz    schedule 23.12.2013