Сегодня я столкнулся с запутанной ситуацией, которая, я надеюсь, кто-нибудь сможет мне объяснить.
У меня есть программа на C ++ с 4 классами:
- A
Base
класс, который действует как общий интерфейс,
- Класс
Enroll
, который подклассов Base
и имеет чисто виртуальный метод enroll()
,
- A
Verify
класс, который также является подклассом Base
и имеет чисто виртуальный метод verify()
,
- A
Both
класс, который подклассов и Enroll
и Verify
и обеспечивает реализации для enroll()
и verify()
Вот так:
class Base {
public:
Base () { }
virtual ~Base () { }
};
class Enroll : public virtual Base {
public:
virtual ~Enroll () { }
virtual void enroll () = 0;
};
class Verify : public virtual Base {
public:
virtual ~Verify () { }
virtual void verify () = 0;
};
class Both : public Enroll, public Verify {
public:
virtual ~Both () { }
virtual void enroll () { printf ("Enrolling.\n"); }
virtual void verify () { printf ("Verifying.\n"); }
};
Экземпляры Both
создаются в функции, не являющейся членом, которая просто создает новый Both
и возвращает указатель:
Both* createInstanceOfBoth () {
return new Both();
}
Наконец, есть класс Registry
, который в основном действует как фабрика Enroll
/ Verify
. Он использует пару указателей на функцию createInstanceOfBoth()
для предоставления экземпляра Enroll
или Verify
:
typedef Enroll* (*EnrollGenerator) ();
typedef Verify* (*VerifyGenerator) ();
class Registry {
public:
Registry () {
enrollGenerator = (EnrollGenerator)&createInstanceOfBoth;
verifyGenerator = (VerifyGenerator)&createInstanceOfBoth;
}
Enroll* getEnroll () { return enrollGenerator (); }
Verify* getVerify () { return verifyGenerator (); }
EnrollGenerator enrollGenerator;
VerifyGenerator verifyGenerator;
};
А вот и проблема. Когда я вызываю getEnroll()
для моего Registry
объекта и вызываю enroll()
для возвращаемого объекта, я вижу правильный, ожидаемый вывод: Enrolling.
. Но когда я вызываю getVerify()
и вызываю verify()
для возвращаемого объекта, метод enroll()
выполняется снова !
Код:
int main () {
Registry registry;
Enroll *enroller;
Verify *verifier;
enroller = registry.getEnroll ();
verifier = registry.getVerify ();
enroller->enroll ();
verifier->verify ();
return 0;
}
Выход:
Enrolling.
Enrolling.
Я заметил, что если я изменю порядок Enroll
и Verify
в объявлении класса Both
(class Both : public Verify, public Enroll {...}
), произойдет противоположный эффект:
Verifying.
Verifying.
Я нашел обходной путь, в котором вместо использования одной функции createInstanceOfBoth()
для создания моих объектов Enroll
и Verify
я использую две функции с разными именами с одинаковым телом, но разными типами возвращаемых данных:
Enroll* createInstanceOfEnroll () {
return new Both();
}
Verify* createInstanceOfVerify () {
return new Both();
}
Затем в классе Registry вместо этого я создаю указатели на эти функции:
Registry () {
enrollGenerator = &createInstanceOfEnroll;
verifyGenerator = &createInstanceOfVerify;
}
Когда я сейчас запускаю программу, я получаю ожидаемый результат:
Enrolling.
Verifying.
У меня такой вопрос: почему не работает первый способ? Я подозреваю, что это как-то связано с приведением createInstanceOfBoth()
к указателю на функцию с другим типом возвращаемого значения, но я не до конца понимаю, что происходит. Сейчас я в порядке с использованием моего обходного пути, но мне любопытно: есть ли способ сделать это с помощью только одной функции createInstanceOfBoth()
вместо необходимости использовать две идентичные функции, но с разными типами возврата?
Чтобы сэкономить время и хлопоты, собрав их в скомпилированный пример, я разместил этот код на https://gist.github.com/833304 для загрузки.