【问题标题】:Why can I call instance functions statically?为什么我可以静态调用实例函数?
【发布时间】:2015-07-23 02:07:29
【问题描述】:

我最近在 GitHub 上查看 Notepad++ 源代码,came across 一个方法调用是这样的:

Window::init(hInst, parent);

我搜索了它所引用的函数,发现了一个Window 类- 但init 函数被标记为virtual,所以很明显它是非静态的。以为我犯了一个错误,我检查了整个标头以确保没有init 的静态重载,并确保没有Window.cpp 文件。没有。

在查看源代码 15 分钟后,我放弃了并在本地 git cloned 存储库,以便我可以在 Visual Studio 中打开它。我做的第一件事是构建,只是为了确保这不是代表项目开发人员的意外合并——构建成功。

我采取的后续步骤:

  • 我打开了调用Window::init 的文件并在Window 上单击了Go To Declaration。它带我去Window 课程。

  • 我在init 函数上单击了Go To Declaration。它指向了虚拟方法的签名。

  • 我将Window.h 文件复制并粘贴到一个全新的标题中,并将Window 的所有引用替换为Foo。当我输入Foo::init 时,编译器会抱怨“非静态成员引用必须与特定对象相关”。

TL;DR: 不知何故,Notepad++ 源代码静态调用了一个非静态方法,然后构建。不适用于任何其他类。证明herehere

我花了 2 个小时盯着这个,但我仍然不明白这怎么可能。我错过了什么吗?

【问题讨论】:

  • 这就是你在重写虚函数中调用基函数的方式。

标签: c++ oop static instance


【解决方案1】:

不,它不是调用静态函数。它只是调用基类的init() 版本。基本上,在tClassName::f 中,您问的是“我想在类tClassName 中调用特定版本的虚函数f()”。

通常,在派生类中调用基类对应的虚函数是很常见的。例如,工厂方法模式:

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

【讨论】:

    【解决方案2】:

    我错过了什么吗?

    是的 - 上下文。 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 正在调用 init() 的基类 Window 版本。

    【讨论】:

    • 啊,真不敢相信我错过了,特别是考虑到课程的名称是Notepad_plus_Window。谢谢!
    【解决方案3】:

    也许这会减少你的困惑。您缺少上下文,这并不是您自己的真正过错。

    您没有在通话中看到隐含的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;
    }
    

    在上面,我们可以指定调用类层次结构中特定方法的对象。

    【讨论】:

      【解决方案4】:

      它不是一个静态函数。它正在调用具有指定(类)范围的函数。

      默认情况下,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;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-08
        • 1970-01-01
        • 1970-01-01
        • 2012-02-17
        • 1970-01-01
        • 2023-03-04
        • 2015-11-13
        相关资源
        最近更新 更多