Я не понимаю, почему 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 ответа
Когда вы создаете переменную 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
.
Конструктор B
сначала вызывает конструктор A
. Это заменяет a_instance
, который вы уже создали.
Эта цепочка конструкторов четко определена в стандарте. База всегда вызывается первой, так что производный конструктор гарантированно работает с допустимым базовым объектом.
То, что вы испытываете, вызвано ошибкой проектирования, которая основана на том, что конструктор 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();
}
Что выводит:
Из базового класса
Из производного класса
Конструктор B
вызывает конструктор A
...
Похожие вопросы
Связанные вопросы
Новые вопросы
c++
C++ — это язык программирования общего назначения. Изначально он разрабатывался как расширение C и имел аналогичный синтаксис, но теперь это совершенно другой язык. Используйте этот тег для вопросов о коде, который будет скомпилирован с помощью компилятора C++. Используйте тег версии для вопросов, связанных с конкретной стандартной версией [C++11], [C++14], [C++17], [C++20] или [C++23]. и т.д.