RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 637510
Accepted
ampawd
ampawd
Asked:2020-03-10 05:04:19 +0000 UTC2020-03-10 05:04:19 +0000 UTC 2020-03-10 05:04:19 +0000 UTC

在不实例化类型的情况下调用非静态方法是安全的

  • 772

考虑以下 C++03 标准的代码:

struct A
{
    A() { cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    void foo() { cout << "foo()" << endl; }
};

int main(int argc, char** argv)
{
    A* pa = (A*)0;

    pa->foo();

    return 0;
}

编译和运行得很好。

出现了以下问题:

1)为什么程序会因为方法的虚拟版本而foo崩溃?

2)如何使用C++11安全调用非静态方法foo,使方法foo有效传递this,从而避免UB使用时?

c++
  • 4 4 个回答
  • 10 Views

4 个回答

  • Voted
  1. Swift - Friday Pie
    2020-03-10T15:31:28Z2020-03-10T15:31:28Z

    1)这是未定义的行为。为什么您编写的内容有效 - 必须在编译器的实现及其创建的代码中寻找答案。提示 - 在一种情况下,在类的主体中声明了一个隐式内联函数。另一方面,当函数是虚函数时,需要有一个初始化的虚表,因为函数的地址存储在其中。该标准不描述实现并将平台视为抽象的冯诺依曼机。

    结果是正确的操作、内存泄漏、完全擦除硬盘驱动器、熔化的处理器,还是从你的鼻子里飞出并开始嘲笑你的恶魔——都没有关系,因为它没有被标准定义。它可以在一个编译器中以这种方式运行,而在另一个编译器中以不同的方式运行,它可以根据编译器的版本而变化,它可以随着每次编译而变化,随着月相的变化,在上级在场的情况下,以及取决于你的心情和上个晴天飞过处理器的中微子数量。或者也许不会改变。

    2)原则上,如果类的实例已经存在,则有一种合法的调用方式。你仍然需要传递一个指向它的指针。你可以用 std::bind 做到这一点。此函数将返回一个可调用对象,该对象将保存绑定到对象实例的方法。如果没有对象,那么什么都没有,如果你不违反标准。

    唯一可以使用空指针的是确定字段的偏移量,但此技术被标准化为 offsetof(在 中定义<cstddef>),其记录可能再次取决于编译器。

    也许您的问题是从静态方法调用非静态方法。当 this 指针已知时(例如,可以传递给静态成员)?这是一个在英文网站上讨论过的例子:

    class Test
    {
     typedef void (Test::*ptrToMemberFunc)();
     ptrToMemberFunc function;
    public:
        Test(int pointTo)
        {
            if (pointTo == 1)
                function = &Test::Function1;
            else
                function = &Test::Function2;
        }
    
        static void CallIt(void* cStyle)
        {
            Test* t(static_cast<Test*>(cStyle));
    
            Test::ptrToMemberFunc pf(t->function);
    
            // Внутренний -> - доступ к полю. Внешний -> передает t как this
            (t->*(t->function))();
        }
    
        void CallIt()
        {
            (this->*function)(); // -> передает this указателю на член
    
        }
    
    private:
        void Function1()
        {
            std::cout << "Function 1" << std::endl;
        }
    
        void Function2()
        {
            std::cout << "Function 2" << std::endl;
        }
    };
    

    在这种情况下,使用了一个非静态指针,它在构造函数中被初始化,这就是奇怪的构造 (t->*(t->function))() 出现的原因;这个例子是在不创建子类的情况下模拟了对虚表的操作,从这段代码中可以明显看出你在描述虚表的功能时落在了哪里。

    • 5
  2. Best Answer
    VladD
    2020-03-10T06:37:03Z2020-03-10T06:37:03Z
    1. 如您所知,您的代码包含未定义的行为。不允许在空指针上调用方法。

      如果编译器将非虚拟方法调用编译this为作为隐藏参数传递的静态定义方法调用,并且您没有在方法代码中取消引用this,并且优化器决定不使用显式 UB,则这可能不会下降。

      同样,如果优化器对您的代码进行流分析,发现它实际上知道参数的确切类型,将其去虚拟化并静态调用该方法,则对虚拟方法的调用可能不会崩溃。或者,如果优化器决定通过虚拟方法表调用,它可能会下降。

      UB,就是,你可能幸运,也可能不幸运。

    2. 按照标准,没办法。非静态方法只有在您手中有一个对象(或指向对象的指针)时才能被调用,该对象的类型与所需的类型匹配或从中派生。你不能this明确地“传递你的”。而既然有了对象,就已经构造好了。

      通过指向另一个对象的指针或空指针进行的调用是 UB。不要这样做。


    另一方面,如果您正在为特定编译器编写代码,您有时可以要求它吞下调用nullptr并在不存在this. 但这是标准范围之外的灰色地带。除非真的有必要,否则不要这样做。

    • 3
  3. Harry
    2020-03-10T13:07:59Z2020-03-10T13:07:59Z

    让我这样说吧——如果一个方法可以在不创建类实例的情况下被调用,那么根据定义,它就是静态的。
    正如 Occam 所说,不要在不必要的情况下增加实体,并使这种方法静态化。
    否则,您的项目类似于问题 - 如何让躯干在没有头的情况下移动。他们说这种情况会发生……但非常不可靠,而且不会持续很长时间。

    • 2
  4. goldstar_labs
    2020-03-10T06:18:33Z2020-03-10T06:18:33Z

    在解决方法中,我只想到了 crtp;示例代码如下所示:

    #include <cstdio>
    #include <iostream>
    
    template<typename T>
    class A
    {
    public:
        A(){};
        ~A(){};
    
        void foo() { static_cast<T*>(this)->foo(); }
    };
    
    class B : public A<B>
    {
    public:
        B(){};
        ~B(){};
    
        void foo() { std::cout << "my leg... my fucking leg... " << std::endl; }
    };
    
    int main(int argc, char *argv[])
    {
    
        A<B>* a = nullptr;
        a->foo();
    
        std::cout << "seems ok" << std::endl;
        return 0;
    }
    

    至于安全性,我希望它是明确的,因为。取消引用空指针就像纯水的 UB :)

    PS 这篇文章是我的恕我直言,我没有在失望中检查它。从编译器的角度来看,一个紫色的静态函数或非静态函数,调用一个非静态函数很可能看起来像这样:A::foo(&a);因此,你可以在那里传递任何 &a,一切都会起作用,直到需要这个,这个是 UB 所在的位置。那些。如果非静态成员函数 foo() 实现静态行为(您可以毫无问题地为它添加前缀static),那么视图构造((A*)nullptr)->foo();是可行的,尽管它在法律上是 UB。

    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    Python 3.6 - 安装 MySQL (Windows)

    • 1 个回答
  • Marko Smith

    C++ 编写程序“计算单个岛屿”。填充一个二维数组 12x12 0 和 1

    • 2 个回答
  • Marko Smith

    返回指针的函数

    • 1 个回答
  • Marko Smith

    我使用 django 管理面板添加图像,但它没有显示

    • 1 个回答
  • Marko Smith

    这些条目是什么意思,它们的完整等效项是什么样的

    • 2 个回答
  • Marko Smith

    浏览器仍然缓存文件数据

    • 1 个回答
  • Marko Smith

    在 Excel VBA 中激活工作表的问题

    • 3 个回答
  • Marko Smith

    为什么内置类型中包含复数而小数不包含?

    • 2 个回答
  • Marko Smith

    获得唯一途径

    • 3 个回答
  • Marko Smith

    告诉我一个像幻灯片一样创建滚动的库

    • 1 个回答
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Алексей Шиманский 如何以及通过什么方式来查找 Javascript 代码中的错误? 2020-08-03 00:21:37 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5