【问题标题】:Why is the vptr discarded when I copy objects? [closed]为什么我复制对象时会丢弃 vptr? [关闭]
【发布时间】:2014-01-22 13:30:14
【问题描述】:

例子

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <iomanip>

struct father
{
    int variable;
    father(){variable=0xEEEEEEEE;};
    virtual void sing(){printf("trollolo,%x\n",variable);}
    ~father(){};
};
struct son:father
{
    son(){variable=0xDDDDDDDD;};
    virtual void sing(){printf("trillili,%x\n",variable);}
    ~son(){};
};
int main()
{
    father * ifather=new(father);
    son * ison=new(son);
    father uncle;
    father * iteachers;

    *((long long*)&uncle)=0xDEAF;
    iteachers=(father*)malloc(20*sizeof(father));

    //ineffective assignments
    iteachers[0]=*ifather;
    uncle=*ifather;

    ifather->sing();//called to prevent optimization
    ison->sing();//only to prevent optimization

    std::cout.setf(std::ios::hex);
    std::cout<<"father:"<<*((long long*)ifather)<<","<<std::endl;
    std::cout<<"teacher0:"<<*((long long*)&(iteachers[0]))<<","<<std::endl;
    std::cout<<"uncle:"<<*((long long*)&uncle)<<","<<std::endl;
    std::cout<<"(son:"<<*((long long*)ison)<<"),"<<std::endl;

//  uncle.sing();//would crash
}

使用 gcc 编译时,teacher[0] 的 vtable 指针为零。 uncle 的 vtable 指针也保持其原始值,而不是被覆盖。 我的问题:为什么会这样? 有干净的解决方法吗?我可以使用uncle._vptr=ifather-&gt;_vptr 并且仍然可以携带吗?复制对象的 ORDINARY 例程是什么?我什至应该提交错误吗? 注意:它应该复制整个对象平台无关,因为无论对象类型如何识别,它都应该始终在对象的数据块内!

文章

Why does my C++ object loses its VPTr

没有帮助我,那一定有不同的原因。

【问题讨论】:

  • 只需使用复制构造函数,停止黑客攻击。请使用std::vectornew 而不是malloc。如果要编写 C 代码,使用 C++ 有什么意义?
  • *((long long*)&amp;uncle)=0xDEAF; 应该做什么?你想完成什么?
  • *((long long*)&amp;uncle)=0xDEAF; 应该只是证明没有任何东西被写入 vptr 位置 - 您留在那里的任何 trash 都会被保留。编辑:这意味着复制例程不会从指针指向的数据块的开头开始 - 非常意外!
  • 您想知道为什么未定义的程序“行为不端”吗?
  • 如果 vtable 指针被复制,我会感到非常惊讶,因为它们无法更改(这将需要更改对象的动态类型,这是不可能的)。

标签: c++ internals vptr


【解决方案1】:

据我了解,基本上问题是这段代码是否:

#include <iostream>
using namespace std;

struct Base
{
    virtual void sing() { cout << "Base!" << endl; }
    virtual ~Base() {}
};

struct Derived: Base
{
    void sing() override { cout << "Derived!" << endl; }
};

auto main()
    -> int
{
    Base* p = new Derived();
    *p = Base();
    p->sing();      // Reporting "Base" or "Derived"?
}

应该报告“Base”或“Derived”。

简而言之,赋值不会改变对象的类型

因此,它报告“派生”。

【讨论】:

  • 啊啊啊,这是一个很好的答案!它通过语言的定义解释了原因!意思是:你可以复制内容,但你永远不能改变一个现有的对象,除非你为它分配新的内存。好的。这是一个好点。它使实施更容易。但对我来说,这意味着我必须编写非常不整洁的代码或坚持使用自制的面向对象实现才能操作现有对象的类。非常感谢!干杯 - Ohnemichel
  • 这是为什么?因为您使用一些默认的提供者赋值运算符将 Base 复制到 Derived 中?
  • 否 - 因为我无法将对象填充到预先分配的数组中。使用复制构造函数没有帮助。或者有没有办法将复制构造函数与 new 运算符一起使用?那么它就可以工作了。
  • @Ohnemichel:有一种方法可以做您所说的事情,使用数组作为对象的 storage,并通过放置在该存储中构造对象new。但是你最好使用union。我提倡这一点,只是提到一般的好消息:c ++并不缺乏轻松和不经意间让你的脚掉下来的方法...... ;-)
  • 是的,这就是解决方案 - 放置新操作符。有一种方法可以使用放置 new 运算符调用复制构造函数。复制构造函数必须像father(const father &amp; ifather){*this=ifather;}; 赋值必须是new(&amp;uncle)father(*ifather); 之前不知道placement new 运算符。重要的是要知道,如果将对象数组与包含结构一起复制,则甚至不会复制 vtable 指针。我想我将不得不递归地做这个放置新的复制构造函数的事情。
猜你喜欢
  • 2011-07-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-03
  • 2011-07-03
  • 1970-01-01
  • 2020-01-17
相关资源
最近更新 更多