Рефакторинг листового класса в базовый класс и сохранение его также в качестве реализации интерфейса - PullRequest
1 голос
/ 15 июня 2010

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

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

Я написал этот код, чтобы проверить идею о том, как реализовать это, и пока он работает, я не уверен, что он мне нравится:

#include <iostream>

// Original code is:
// IBase -> Derived1

// I need to refactor Derive2 to be both indipendet class
// and programmers should also be able to use the interface class
// Derived2 -> MyClass + IBase
// MyClass


class IBase {
public:
    virtual void printMsg() = 0;
};

///////////////////////////////////////////////////
class Derived1 : public IBase {
public:
    virtual void printMsg(){ std::cout << "Hello from Derived 1" << std::endl; }
};


//////////////////////////////////////////////////
class MyClass {
public:
    virtual void printMsg(){ std::cout << "Hello from MyClass" << std::endl; }
};

class Derived2: public IBase, public MyClass{
    virtual void printMsg(){ MyClass::printMsg(); }
};

class Derived3: public MyClass, public IBase{
    virtual void printMsg(){ MyClass::printMsg(); }
};

int main()
{
    IBase *o1 = new Derived1();
    IBase *o2 = new Derived2();
    IBase *o3 = new Derived3();
    MyClass *o4 = new MyClass();

    o1->printMsg();
    o2->printMsg();
    o3->printMsg();
    o4->printMsg();

    return 0;
}

Вывод работает как положено (проверено с использованием gcc и clang,2 различные реализации C ++, поэтому я думаю, что здесь я в безопасности):

[elcuco@pinky ~/src/googlecode/qtedit4/tools/qtsourceview/qate/tests] ./test1
Hello from Derived 1
Hello from MyClass
Hello from MyClass
Hello from MyClass
[elcuco@pinky ~/src/googlecode/qtedit4/tools/qtsourceview/qate/tests] ./test1.clang 
Hello from Derived 1
Hello from MyClass
Hello from MyClass
Hello from MyClass

Вопрос:

Мой оригинальный код:

class Derived3: public MyClass, public IBase{
    virtual void IBase::printMsg(){ MyClass::printMsg(); }
};

Это то, что я хочу выразить, но это не компилируется.Должен признать, я не до конца понимаю, почему этот код работает, так как я ожидаю, что новый метод Derived3::printMsg() будет реализацией MyClass::printMsg(), а не IBase::printMsg() (даже если это то, что я делаю want).

Как компилятор выбирает метод для повторной реализации, когда два "родственных класса" имеют одинаковое имя виртуальной функции?

Если у кого-то есть лучший способ реализовать это,Я также хотел бы знать:)

Ответы [ 2 ]

1 голос
/ 15 июня 2010

Ответ таков: компилятор переопределяет обе функции, как показано в следующем примере:

#include <cstdio>
using std::printf;

class A {
public:
    virtual void a() {
        printf("A::a\n");
    }
};
class B {
public:
    virtual void a() {
        printf("B::a\n");
    }
};
class C : public A, public B {
public:
    virtual void a() {
        printf("C::a\n");
        A::a();
        B::a();
    }
};
int main() {
    C c;
    A &a = c;
    B &b = c;
    printf("Calling C::a via A\n");
    a.a();
    printf("Calling C::a via B\n");
    b.a();
}

Вывод:

Calling C::a via A
C::a
A::a
B::a
Calling C::a via B
C::a
A::a
B::a

Если вы хотите переопределить одно, а не другое, вам нужно переименовать его. Он не только будет делать то, что вы хотите, но и будет яснее.

0 голосов
/ 15 июня 2010

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

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

...