2014-01-22 8 views
-3

ПримерПочему vptr отбрасывается при копировании объектов?

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

указатель виртуальных таблиц учителей [0] равно нуль при компиляции с GCC. Также указатель vtable дяди сохраняет свое первоначальное значение вместо того, чтобы быть перезаписанным. Мои вопросы: Почему это так? Есть ли CLEAN обходной путь? Могу ли я пойти с uncle._vptr=ifather->_vptr и все еще быть портативным? Какова процедура ORDINARY для копирования объекта? Должен ли я даже регистрировать ошибку? Примечание: он должен скопировать всю платформу независимо от платформы, поскольку независимо от того, как выполняется идентификация типа объекта, поскольку она всегда должна находиться внутри блока данных объекта!

В статье

Why does my C++ object loses its VPTr

не помогли мне, что должны иметь разные причины.

+2

Просто используйте конструктор копирования, прекратите взломать. И используйте 'std :: vector' и' new' вместо 'malloc'. Какой смысл использовать C++, если вы собираетесь писать C-код? –

+1

Что такое '* ((long long *) & дядя) = 0xDEAF;' предполагается делать ?? Что вы пытаетесь достичь? – interjay

+0

'* ((long long *) & дядя) = 0xDEAF;' должен просто продемонстрировать, что только ничего не записывается в местоположение vptr - любой * мусор *, который вы оставляете, сохраняется. Изменить: И это означает, что процедура копирования не начинается в начале блока данных, куда указатель направляется - очень неожиданно! – Ohnemichel

ответ

3

Как я понимаю, в основном вопрос, является ли этот код:

#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» или «Производная».

Короче говоря, присвоение не изменяет тип объекта.

Следовательно, он сообщает «Производные».

+0

AAAAhh, это отличный ответ! Это объясняет причину определениями языка! Средства: вы можете скопировать содержимое, но вы никогда не сможете изменить существующий объект, если вы не выделили для него новую память. хорошо. Это хороший момент. Это упрощает реализацию. Но для меня это будет означать, что я должен написать ОЧЕНЬ неопрятный код или вернуться к самодельной объектно-ориентированной реализации, чтобы иметь возможность манипулировать классом существующего объекта. Большое спасибо! Cheers - Ohnemichel – Ohnemichel

+0

Почему это? Поскольку вы скопировали базу в производную, используя какой-либо оператор назначения поставщика по умолчанию? – paulm

+0

Нет - потому что я не могу заполнить объекты в заранее выделенный массив. Использование конструктора копирования не помогает. Или есть способ использовать конструктор копирования с новым оператором? то это сработает. – Ohnemichel