Я не понимаю, почему p->a() звонит B::a() ?. Есть ли где-нибудь в документации / стандарте C ++ параграф, который хорошо описывает это поведение?

#include <iostream>
using namespace std;

class A {
  public:
  A() { cout << "A ctor" << endl; a_instance = this; }
  static A *get_instance() { return a_instance; }
  static A *a_instance;
  void virtual a() { cout << "From base class" << endl; }
};

class B : public A {
public:
  B() { cout << "B ctor" << endl; b_instance = this; }
  static B *get_instance() { return b_instance; }
  static B *b_instance;
  void virtual a() { cout << "From derived class" << endl; }
};

A *A::a_instance = 0;
B *B::b_instance = 0;

main()
{
    cout << "Create A" << endl;
    A ab;
    cout << "Create B" << endl;
    B abc;
    B *ptr = B::get_instance();
    A *p = A::get_instance();

    cout << "Called from A object type" << endl;
    if (p) {
       p->a();
    }
}
4
JohnX 25 Мар 2014 в 18:41

4 ответа

Лучший ответ

Когда вы создаете переменную abc, конструктор A устанавливает a_instance для этого экземпляра. Несмотря на то, что p является указателем на A, поскольку экземпляр указывает на B, он правильно вызывает B::a().

Чтобы исправить это поведение, вы можете использовать следующее:

A* A::get_instance()
{
    static A a;
    return &a;
}

B* B::get_instance()
{
    static B b;
    return &b;
}  

И удалите весь код, связанный с a_instance и b_instance.

6
Bart van Nierop 25 Мар 2014 в 19:06
Спасибо, что скопировали мой ответ.
 – 
LihO
25 Мар 2014 в 19:08

Конструктор B сначала вызывает конструктор A. Это заменяет a_instance, который вы уже создали.

Эта цепочка конструкторов четко определена в стандарте. База всегда вызывается первой, так что производный конструктор гарантированно работает с допустимым базовым объектом.

5
Mark Ransom 25 Мар 2014 в 18:45

То, что вы испытываете, вызвано ошибкой проектирования, которая основана на том, что конструктор A инициализирует статический член с помощью this. Тело этого конструктора вызывается не только при создании экземпляра A, но и при создании экземпляра любого из его производных классов:

A() { /* ... */ a_instance = this; }

Поэтому, когда вы создаете экземпляр типа B, до того, как тело конструктора B выполняется, сначала выполняется тело конструктора A - это перезаписывает член a_instance с this в контексте экземпляра типа B, то есть заставляет a_instance указывать на этот новый экземпляр типа B.


Что вы можете сделать, так это поместить статическую переменную внутри метода getInstance:

class A
{
public:
    static A* getInstance() {
        static A s;                 // <-- instantiated upon first call
        return &s;
    }
    void virtual foo() { std::cout << "From base class" << std::endl; }

protected:
    A() { }                         // <-- protected constructor
    ~A() { }
    A(const A&);                    // <-- protected copy constructor
    A& operator=(const A&);         // <-- protected assignment operator
};

Затем B:

class B : public A
{
public:
    static B* getInstance() {
        static B s;                 // <-- instantiated upon first call
        return &s;
    }
    void virtual foo() { std::cout << "From derived class" << std::endl; }

protected:
    B() { }                         // <-- protected constructor
    ~B() { }
    B(const B&);                    // <-- protected copy constructor
    B& operator=(const B&);         // <-- protected assignment operator
};

И возможное использование:

int main() {
    A::getInstance()->foo();
    B::getInstance()->foo();
}

Что выводит:

Из базового класса
Из производного класса

4
LihO 25 Мар 2014 в 19:07

Конструктор B вызывает конструктор A ...

2
CiaPan 25 Мар 2014 в 18:46