Недавно я просматривал исходный код Notepad ++ на GitHub, и наткнулся вызов метода, как это:
Window::init(hInst, parent);
Я искал функцию, на которую он ссылался, и наткнулся на Window
класс- но init
функция была отмечена virtual
так ясно это было нестатично. Думая, что я сделал ошибку, я проверил весь заголовок, чтобы убедиться, что не было статической перегрузки init
и я убедился, что не было Window.cpp
файл. Нет
Пройдя 15 минут вокруг источника, я сдался и git cloned
репо локально, чтобы я мог открыть его в Visual Studio. Первое, что я сделал, это собрал, чтобы убедиться, что это не случайное слияние от имени разработчиков проекта — сборка прошла успешно.
Следующие шаги, которые я предпринял:
Я открыл файл вызова Window::init
и нажал Go To Declaration
на Window
, Это берет меня к Window
учебный класс.
Я нажал Go To Declaration
на init
функция. Это указывает мне на подпись виртуального метода.
Я копирую и вставляю Window.h
файл в совершенно новый заголовок и заменить все ссылки Window
с Foo
, Когда я набираю Foo::init
компилятор жалуется, что «нестатическая ссылка на элемент должна относиться к определенному объекту».
TL; DR: Каким-то образом исходный код Notepad ++ статически вызывает нестатический метод, и это создает. Не работает ни с одним другим классом. доказательство Вот а также Вот.
Я потратил на это 2 часа, но до сих пор не понимаю, как это возможно. Я что-то пропустил?
Нет, это не вызов статической функции. Это просто вызов версии базового класса init()
, В основном в tClassName::f
, вы спрашиваете: «Я хочу вызвать эту конкретную версию виртуальной функции f()
в классе tClassName
».
Обычно довольно часто вызывать аналог базового класса виртуальной функции в производном классе. Например, шаблон фабричного метода:
#include "tObject.h"#include "tObject1.h" // public inheritance from tObject
#include "tObject2.h" // public inheritance from tObject
#include "tObject3.h" // public inheritance from tObject
class BaseFactory
{
public:
// factory method
virtual tNode *createObject(int id)
{
if (id == 1) return new tObject1;
else return new tObject2;
}
};
class DerivedFactory: public BaseFactory
{
public:
virtual tNode *createObject(int id)
{
// Overrides the default behavior only for one type
if (id == 1) return new tObject3;
// Call the default factory method for all other types
else return BaseFactory::createObject(id);
}
};
Я что-то пропустил?
Да — контекст. Notepad_plus_Window
происходит от Window
и призыв к Window::init()
находится внутри Notepad_plus_Window::init()
метод:
class Notepad_plus_Window : public Window {
public:
...
void init(HINSTANCE, HWND, const TCHAR *cmdLine, CmdLineParams *cmdLineParams);
...
};
void Notepad_plus_Window::init(HINSTANCE hInst, HWND parent, const TCHAR *cmdLine, CmdLineParams *cmdLineParams)
{
...
Window::init(hInst, parent);
...
}
В данном контексте, Notepad_plus_Window
вызывает базовый класс Window
версия init()
,
Может быть, это будет вас смущать меньше. Вы пропускаете контекст, не по своей вине.
Вы не видите неявное this
в вызове.
Возьмите следующий пример:
#include <cstdio>
#include <iostream>
class Foo {
public:
virtual void bar() {
std::cout << "Foo" << std::endl;
}
};
class Bar : public Foo {
public:
virtual void bar() {
std::cout << "Bar" << std::endl;
}
};
int main() {
Bar bar;
bar.bar(); //-> bar
bar.Foo::bar(); //-> foo
Bar *barp = &bar;
barp->bar(); //-> bar
barp->Foo::bar(); //-> foo
return 0;
}
Выше мы можем указать объект, для которого вызывается определенный метод в иерархии классов.
Это не статическая функция. Это вызов функции с указанной (классовой) областью действия.
По умолчанию init () будет соответствовать функциям в пределах текущей области класса, если они существуют. это неявный вызов this, равен this-> init (),
Но с указанным префиксом класса / пространства имен вы можете явно вызывать любую конкретную функцию без динамического связывания. Т.е. :: init () вызовет функцию init () в глобальной области видимости.
следующий код может дать вам лучшее понимание
#include <iostream>
class A
{
public:
virtual void test()
{
std::cout << "A" << std::endl;
}
};
class B : public A
{
public:
virtual void test()
{
std::cout << "B" << std::endl;
}
};
int main()
{
A* a = new B();
a->A::test();
return 0;
}