Frage

I'm getting confused why p->a() is calling B::a()?. Is there a paragraph somewhere in the C++ documentation/standard that describes this behavior well?

#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();
    }
}
War es hilfreich?

Lösung

When you create the variable abc, A's constructor sets a_instance to that instance. Despite p being a pointer to an A, since the instance is pointing to a B, it's correctly calling B::a().

To fix this behaviour, you could use the following:

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

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

and remove all code that has to do with a_instance and b_instance.

Andere Tipps

The B constructor calls the A constructor first. That replaces the a_instance that you'd already created.

This chaining of constructors is well defined in the standard. The base is always called first, so that the derived constructor is guaranteed to be working on a valid base object.

What you are experiencing is caused by the design error, which is based on A's constructor initializing the static member using this. Body of this constructor is invoked not only when you are creating the instance of A but also when you are creating the instance of any of its derived classes:

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

So when you create an instance of type B, before the body of B's constructor is executed, the body of A's constructor is executed at first - this overwrites member a_instance with this within a context of instance of type B, i.e. it makes a_instance to point to this new instance of type B.


What you could do is to place a static variable inside of the getInstance method:

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
};

then 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
};

and possible usage:

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

that outputs:

From base class
From derived class

B constructor invokes A constructor...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top