草庐IT

c++ - 通过命名成员调用虚拟与地址或引用的区别

coder 2024-02-23 原文

更新如下:在 clang 中,通过其名称使用多态对象的左值不会激活虚拟分派(dispatch),但会通过其地址激活。

对于下面的基类B和派生D,虚函数something, union Space

#include <iostream>
using namespace std;

struct B {
    void *address() { return this; }
    virtual ~B() { cout << "~B at " << address() << endl; }
    virtual void something() { cout << "B::something"; }
};

struct D: B {
    ~D() { cout << "~D at " << address() << endl; }
     void something() override { cout << "D::something"; }
};

union Space {
    B b;
    Space(): b() {}
    ~Space() { b.~B(); }
};

如果您的 s 值为 Space,在 Clang++ 中:(更新:错误地声称 g++ 具有相同的行为) 如果您执行 s.b.something(),将调用 B::something(),而不是对 s.b 执行动态绑定(bind),但是,如果您调用 (&s.b)->something() 将动态绑定(bind)到 b 真正包含的内容(BD). 完成代码是这样的:

union SpaceV2 {
    B b;
    SpaceV2(): b() {}
    ~SpaceV2() { (&b)->~B(); }
};

static_assert(sizeof(D) == sizeof(B), "");
static_assert(alignof(D) == alignof(B), "");

#include <new>

int main(int argc, const char *argv[]) {
    {
        Space s;
        cout << "Destroying the old B: ";
        s.b.~B();
        new(&s.b) D;
        cout << "\"D::something\" expected, but \"";
        s.b.something();
        cout << "\" happened\n";
        auto &br = s.b;
        cout << "\"D::something\" expected, and \"";
        br.something();
        cout << "\" happened\n";
        cout << "Destruction of D expected:\n";
    }
    cout << "But did not happen!\n";
    SpaceV2 sv2;
    new(&sv2.b) D;
    cout << "Destruction of D expected again:\n";
    return 0;    
}

当使用 -O2 优化编译并运行程序时,这是输出:

$./a.out 
Destroying the old B: ~B at 0x7fff4f890628
"D::something" expected, but "B::something" happened
"D::something" expected, and "D::something" happened
Destruction of D expected:
~B at 0x7fff4f890628
But did not happen!
Destruction of D expected again:
~D at 0x7fff4f890608
~B at 0x7fff4f890608

令我惊讶的是,使用 placement new 设置 s.b 的动态类型会导致通过其名称或通过其名称在完全相同的左值上调用 something 的差异地址。第一个问题很重要,但我一直没能找到答案:

  1. 是否根据 C++ 标准对派生类放置新的,如 new(&s.b) D 未定义的行为
  2. 如果不是未定义的行为,那么这种不通过命名成员的左值激活虚拟分派(dispatch)的选择是在标准中指定的东西还是在 G++、Clang 中的选择?

谢谢,我在 S.O. 中的第一个问题曾经。

更新 引用标准的答案和评论是准确的:根据标准,s.b 将永远引用确切类型 B 的对象,内存 允许更改类型,但是通过 s.b 对该内存的任何使用都是“未定义行为”,即被禁止,或者编译器可以随心所欲地进行翻译。如果 Space 只是一个字符缓冲区,就地构造、析构和更改类型都是有效的。在导致这个问题的代码中正是这样做的,它适用于符合标准的 AFAIK。 谢谢。

最佳答案

表达式 new(&s.b) D; 重新使用名为 s.b 且之前被 B 占用的存储用于存储一个新的 D

但是您随后编写了 s.b.something(); 。这会导致未定义的行为,因为 s.b 表示 B,但存储在该位置的实际对象是 D。参见 C++14 [basic.life]/7:

If, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object and, once the lifetime of the new object has started, can be used to manipulate the new object, if:

  • the storage for the new object exactly overlays the storage location which the original object occupied, and

  • the new object is of the same type as the original object (ignoring the top-level cv-qualifiers), and

    [...]

不满足最后一个要点,因为新类型不同。

(代码后面还有其他潜在问题,但由于此处导致了未定义的行为,因此它们没有实际意义;您需要进行重大设计更改才能避免此问题)。

关于c++ - 通过命名成员调用虚拟与地址或引用的区别,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41456172/

有关c++ - 通过命名成员调用虚拟与地址或引用的区别的更多相关文章

  1. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  2. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  3. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  4. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  5. ruby - 通过 RVM (OSX Mountain Lion) 安装 Ruby 2.0.0-p247 时遇到问题 - 2

    我的最终目标是安装当前版本的RubyonRails。我在OSXMountainLion上运行。到目前为止,这是我的过程:已安装的RVM$\curl-Lhttps://get.rvm.io|bash-sstable检查已知(我假设已批准)安装$rvmlistknown我看到当前的稳定版本可用[ruby-]2.0.0[-p247]输入命令安装$rvminstall2.0.0-p247注意:我也试过这些安装命令$rvminstallruby-2.0.0-p247$rvminstallruby=2.0.0-p247我很快就无处可去了。结果:$rvminstall2.0.0-p247Search

  6. ruby-on-rails - Enumerator.new 如何处理已通过的 block ? - 2

    我在理解Enumerator.new方法的工作原理时遇到了一些困难。假设文档中的示例:fib=Enumerator.newdo|y|a=b=1loopdoy[1,1,2,3,5,8,13,21,34,55]循环中断条件在哪里,它如何知道循环应该迭代多少次(因为它没有任何明确的中断条件并且看起来像无限循环)? 最佳答案 Enumerator使用Fibers在内部。您的示例等效于:require'fiber'fiber=Fiber.newdoa=b=1loopdoFiber.yieldaa,b=b,a+bendend10.times.m

  7. ruby - 触发器 ruby​​ 中 3 点范围运算符和 2 点范围运算符的区别 - 2

    请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是

  8. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

  9. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

  10. ruby - 从 Ruby 中的主机名获取 IP 地址 - 2

    我有一个存储主机名的Ruby数组server_names。如果我打印出来,它看起来像这样:["hostname.abc.com","hostname2.abc.com","hostname3.abc.com"]相当标准。我想要做的是获取这些服务器的IP(可能将它们存储在另一个变量中)。看起来IPSocket类可以做到这一点,但我不确定如何使用IPSocket类遍历它。如果它只是尝试像这样打印出IP:server_names.eachdo|name|IPSocket::getaddress(name)pnameend它提示我没有提供服务器名称。这是语法问题还是我没有正确使用类?输出:ge

随机推荐