Передача указателя на базовый класс функциям-членам производного класса в C ++ - PullRequest
1 голос
/ 15 декабря 2009

Как получить доступ к данным производного класса из функции-члена одного из его экземпляров, которая получает указатель на базовый класс? У меня есть:

    class Base{
        public:
            virtual void compare(Base*)=0;
    };

    class A: public Base{
        int x;
        public:
            A();
            void compare(Base*);
    };

    class B: public Base{
        char c;
        public:
            void compare(Base*);
    };

    A::A(){
        x = 1;
    };

    void A::compare(Base *p){
        cout<< "A's x is " << x << "\n";
        cout<< "Argument's x is " << p->x;
    };

    void B::compare(Base* p){
        cout<< "B's compare() is running\n";
    };

    int main(const int argc, const char *argv[]){
        A a1 = A();
        A a2 = A();
        B b = B();
        a1.compare(&a2);
    }

, который не будет работать, поскольку Base не имеет элемента данных x. Когда я писал это, мне пришло в голову, что это не очень хорошая идея для доступа к члену данных производного класса через указатель на класс интерфейса Base.

В моей задаче мне нужно сравнить различные производные классы с одним и тем же интерфейсом, предоставленным классом Base. Интерфейс Base должен включать compare(Base*) функцию-член. Способ сравнения экземпляров может отличаться в зависимости от производных. Логика сравнения, по-видимому, должна быть закодирована внутри функций-членов этих классов. Как мне реализовать эту модель?

Ответы [ 6 ]

4 голосов
/ 15 декабря 2009

Если ваши производные классы не разделяют некоторые общие данные, которые вы хотите сравнить, с которыми вы можете получить доступ через базовый класс, для этого нет простого решения.
Например, динамическое приведение в каждом производном классе является избыточным, медленным и подверженным ошибкам, если у вас не очень небольшое количество производных классов.

Одним из решений для иерархий больших типов является двойная отправка .

1 голос
/ 15 декабря 2009

Я должен был пробиться через это некоторое время назад. В моем случае конкретные требования требовали, чтобы два элемента сравнивались одинаково только тогда, когда они на самом деле были одного и того же точного типа. То есть вы хотите получить положительное совпадение только при сравнении яблок с яблоками, но на самом производном уровне (золотые яблоки с золотыми яблоками, а не с королевскими праздничными яблоками.

class base {
public:
   virtual bool compare( base const & rhs ) = 0;
};
class derived : public base {
public:
   virtual bool compare( base const & rhs ) {
      if ( typeid(*this) != typeid(rhs) ) return false;
      return *this == static_cast<derived const &>(rhs);
   }
};
bool operator==( derived const & lhs, derived const & rhs );

Обратите внимание, что в итоге мы использовали typeid + static_cast вместо более распространенной идиомы динамических приведений, чтобы заставить оба типа быть абсолютно одинаковыми. Если бы мы использовали dynamic_cast, то симметрия могла бы нарушиться и:

class rederived : public derived {
public:
   virtual bool compare( base const & rhs ) {
      rederived const * p = dynamic_cast<rederived const *>(&rhs);
      return p && *this == *p;
   }
};
void test() {
   derived d;
   rederived rd;
   d.compare( rd ); // compare if derived subobject is equal, possibly true
   rd.compare( d ); // always false, d cannot be dyncamically casted to rederived
}

Опять же, это просто из-за наших конкретных ограничений, которые требовали одинакового типа для обоих объектов. Если вы просто хотите проверить, являются ли они «совместимыми» на одном уровне иерархии (что само по себе является асимметричной операцией, так как левая часть определяет, на каком уровне иерархии вы хотите выполнить сравнение).

1 голос
/ 15 декабря 2009

dynamic_cast.

struct Base
{
    virtual bool compare(const Base*) = 0;
};

struct A : Base
{
    A()
    {
        x = 1;
    }
    virtual bool compare(const Base* other)
    {
        const A* rhs(dynamic_cast<const A*>(other));
        if(other == NULL) { return false; }
        return x == rhs->x;
    }
private:
    int x;
};

struct B : Base
{
    virtual bool compare(const Base* other)
    {
        const B* rhs(dynamic_cast<const B*>(other));
        if(other == NULL) { return false; }
        return c == rhs->c;
    }
private:
    char c;
};
1 голос
/ 15 декабря 2009

Вы можете хромать домой с этим, в зависимости от вашего объекта heirarchy:

bool A::compare(Base *p){
    A *pa = dynamic_cast<A*>(p);
    if (!pa) return false;
    return x == pa->x;
};

На самом деле, существуют серьезные трудности с реализацией полиморфных сравнений. Не наименьшая проблема заключается в том, что если a является экземпляром A, а c является экземпляром некоторого производного класса C из A, то с помощью этой техники вы можете легко получить a.compare (c), равную true, но c.compare (а) быть ложным.

Вы могли бы быть лучше либо:

  • , чтобы сделать ваши сравнения неполиморфными и поместить их в вызывающую программу для сравнения их как типа Base, только если все, что их волнует, - это то, что они равны ", насколько Base может сказать". Но это менее полезно для вызывающей стороны и совершенно бессмысленно, если в Base нет элементов данных, как в этом примере.
  • Имеет не виртуальное сравнение в Base, которое проверяет, равны ли два объекта typeid, возвращает false, если они этого не делают, и вызывает виртуальную функцию сравненияImpl для двух из них, если они есть. Тогда A :: сравнениеImpl знает, что его параметр является экземпляром A, и может static_cast его:

.

class Base {
public:
    bool compare(Base *p) {
        if (typeid(*this) != typeid(*p)) return false;
        return compareImpl(p);
    }
private:
    virtual bool compareImpl(Base *p) = 0;
};

class A : public Base {
    int x;
private:
    bool compareImpl(Base *p) {
        return x == static_cast<A*>(p)->x;
    }
};
1 голос
/ 15 декабря 2009

Вы можете использовать dynamic_cast для приведения базового указателя к одному из производных типов. Просто убедитесь, что правильно обрабатываете случай, когда dynamic_cast не может привести указатель.

Я должен добавить, что это вонючий способ сделать это. Если объекты действительно сопоставимы, они должны быть сопоставимы на уровне базового класса.

0 голосов
/ 15 декабря 2009

Как насчет этого?

int main(const int argc, const char *argv[]){
    Base a1 = new A();
    Base a2 = new A();
    Base b = new B();
    a1.compare(&a2); }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...