-2

Example

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

The vtable pointer of teachers[0] is zero when compiled with gcc. Also the vtable pointer of uncle keeps its original value instead of being overwritten. My questions: Why HAS it be that way? Is there a CLEAN workaround? Can i go with uncle._vptr=ifather->_vptr and still be portable? What is the ORDINARY routine to copy an object? Should I even file a bug? Note: it should copy the whole object platform-independant, because no matter how the identification of the object type is done, since it should always be inside the object's data block!

The article

Why does my C++ object loses its VPTr

didn't help me, that must have a different reason.

Community
  • 1
  • 1
Ohnemichel
  • 324
  • 2
  • 9
  • 2
    Just use a copy constructor, stop hacking. And please use `std::vector` and `new` instead of `malloc`. What's the point of using C++ if you're going to write C code? – Luchian Grigore Jan 22 '14 at 13:32
  • 1
    What is `*((long long*)&uncle)=0xDEAF;` supposed to do?? What are you trying to accomplish? – interjay Jan 22 '14 at 13:33
  • `*((long long*)&uncle)=0xDEAF;` should just demonstrate that just NOTHING is written to the vptr location - any *trash* you leave there is retained. Edit: And that means that the copy routine doesn't start at the beginning of the datablock where the pointer directs to - very unexpected! – Ohnemichel Jan 22 '14 at 13:37
  • Are you wondering why an undefined program is "misbehaving"? – molbdnilo Jan 22 '14 at 13:41
  • I think it is ODD that it is behaving that way. Imagine you want to store an object at a defined memory location - and loose it's vptr. – Ohnemichel Jan 22 '14 at 13:42
  • 1
    I would be very surprised if vtable pointers were copied, since they can't change (that would entail changing the dynamic type of an object, which isn't possible). – molbdnilo Jan 22 '14 at 13:45
  • @Ohnemichel You would use one of the well-defined operations; assignment, copy construction , or placement new, for that. – molbdnilo Jan 22 '14 at 13:47
  • I added father(const father & ifather){}; to the >>father << class. calling iteachers[0]=father(*ifather); on the father class does not help! – Ohnemichel Jan 22 '14 at 13:48
  • @molbdnilo you should present it as answer, not comment – qwm Jan 22 '14 at 13:52
  • Hi @Ohnemichel, this is unrelated, but to avoid UB please try to: (1) never use malloc/free in C++; (2) never use C-style casts in C++; (3) use c++ headers instead of C headers; (4) use C++ iostreams instead of the C stdio API; (5) in classes with at least one virtual function, ALWAYS declare the destructor virtual; (6) Unless in _very_ specific cases, always use std:: containers instead of C pointers to arrays (if you cannot think of any specific cases, _always_ use std::vector or the like). – utnapistim Jan 22 '14 at 13:57
  • Alright. Then NOT using C++ object oriented programming is better for me. Although I just wanted to know WHY this behavior happens, people voted me down so my head is just not suitable for C++, because C++ is taboo on binary level, which I want to regard. Thank you for the hint of declaring the destructor virtual, either – Ohnemichel Jan 22 '14 at 14:04
  • I think you were downvoted not because of your question, but because of the coding style which is hard to read. – qwm Jan 22 '14 at 14:29

1 Answers1

5

As I understand it, basically the question is whether this code:

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

should report "Base" or "Derived".

In short, assignment does not change the type of an object.

Hence, it reports "Derived".

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • AAAAhh, that is a great Answer! It explains the reason by the definitions of the language! Means: you can copy the contents, but you can NEVER change an existing object unless you allocate new memory for it. good. That is a good point. It makes implementation easier. But for me, this will mean that I have to write VERY untidy code or stick back to a self-made object-oriented implementation to be able to manipulate the class of an existing object. Thanks a lot! Cheers - Ohnemichel – Ohnemichel Jan 22 '14 at 13:57
  • Why is this? Because you've copied a Base into a Derived using some default provider assignment operator? – paulm Jan 22 '14 at 13:59
  • No - because I cannot fill the objects into a pre-allocated array. The use of a copy constructor doesn't help. Or is there a way to use the copy constructor with the new operator? then it would work. – Ohnemichel Jan 22 '14 at 14:10
  • @Ohnemichel: there is a way to do what you appear to be talking about, using an array as just *storage* for the object, and constructing objects in that storage via placement `new`. but you would be better off using a `union` then. which i'm ***not*** advocating, just mentioning as general nice-to-know-about: c++ is not lacking in ways to easily and inadvertently shoot your foot off... ;-) – Cheers and hth. - Alf Jan 22 '14 at 15:23
  • Yes, that is the solution - The placement new operator. There IS a way to call a copy constructor with the placement new operator. The copy constructor must be like `father(const father & ifather){*this=ifather;};` The assignment must be `new(&uncle)father(*ifather);` I didn't know the placement new operator before. Important to know, vtable pointers are not even copied if an array of objects is copied along with a containing structure. I guess I will have to do this placement new copy constructor things recursively. – Ohnemichel Jan 22 '14 at 20:11
  • I'll add for people with the same problem as I had that this happens inside unions as well... suppose you have `union Variant { Variant(const Base &base) { derived = base; } Derived derived; };`. In this case, `base._vptr.Base` does not get copied to `derived`, so `derived._vptr.Base` remains NULL and there is a segfault whenever `derived.sing` is called. – bcdan Sep 02 '21 at 14:01