Cast to a Child

前端 未结 4 1748
无人及你
无人及你 2021-01-23 07:47

What I\'m actually trying to do is cast a constructed moneypunct to the punct_facet in this question without writing a copy constructor as in this answ

相关标签:
4条回答
  • 2021-01-23 08:19

    So this is implementation specific as Wikipedia notes:

    The C++ standards do not mandate exactly how dynamic dispatch must be implemented, but compilers generally use minor variations on the same basic model.

    Typically, the compiler creates a separate vtable for each class. When an object is created, a pointer to this vtable, called the virtual table pointer, vpointer or VPTR, is added as a hidden member of this object. The compiler also generates "hidden" code in the constructor of each class to initialize the vpointers of its objects to the address of the corresponding vtable.

    Even worse Danny Kalev states:

    Compilers can be divided into two categories with respect to their vptr's position. UNIX compilers typically place the vptr after the last user-declared data member, whereas Windows compilers place it as the first data member of the object, before any user-declared data members.

    I give all the preceding information to indicate the conditions in which my hack will work:

    1. is_standard_layout fails for your child because it has: "has virtual functions or virtual base classes"
    2. Your parent does not have an copy constructor or an assignment operator (otherwise you could just copy in the parent on construction of the child object)
    3. Your compiler does in fact implement a v-table
    4. Whether your v-table is at the beginning or end of the object layout
    5. The position your compiler assigns to child member variables relative to parent member variables in the object layout

    With understanding of the hack that you are getting into I will now proceed to extend the classes in the question to better demonstrate how we can "Cast to a Child":

    class Parent{
    public:
        Parent operator=(const Parent&) = delete;
        Parent(const Parent&) = delete;
        Parent() = default;
        Parent(int complex) : test(complex) {}
    
        virtual void func(){ cout << test; }
    private:
        int test = 0;
    };
    
    class Child : public Parent{
    public:
        Child operator=(const Child&) = delete;
        Child(const Child&) = delete;
        Child() = default;
        Child(const Parent* parent){
            const auto vTablePtrSize = sizeof(void*);
    
            memcpy(reinterpret_cast<char*>(this) + vTablePtrSize,
                   reinterpret_cast<const char*>(parent) + vTablePtrSize,
                   sizeof(Parent) - vTablePtrSize);
        }
    
        virtual void func(){ cout << "Child, parent says: "; Parent::func(); }
    };
    

    We are simply using memcpy to copying the state of the parent object, while allowing all Child information to persist.

    You can see that this code:

    Parent foo(13);
    Child bar(&foo);
    
    bar.func();
    

    Will print:

    Child, parent says: 13

    Live example and though it's not asked for in the question here's how it could be accomplished for multiple inheritance: http://ideone.com/1QOrMz

    This is a useful solution for moneypunct as it's initialization will be implementation specific anyway, since C++ does not specify any locale names other than:

    • ""
    • "C"
    • "POSIX"

    I would like to close this answer by pointing out that this entire post has been about how to overcome limitations intentionally put in place by the designers of moneypunct. So, yes you can do this but as you do the obvious question should be asked: "Why were the copy constructor and assignment operator of moneypunct deleted in the first place? What aspect of that design am I intentionally subverting?"

    0 讨论(0)
  • 2021-01-23 08:20

    It is not legal to assign address of base object to pointer of derived class.

    If you want to call virtual function in derived class, you have to instantiate an object of derived class and call it thru this object, or pointer (of which type might be base class) to this object.

    Virtual function tables in base and derived class are separated, so you cannot access virtual function of derived class thru an object of base class

    0 讨论(0)
  • 2021-01-23 08:25

    If the parent ISA Child then the Child.func() will be called anyway.

    If the parent is not a Child and you want the Child.func to be called then your design is broken.

    0 讨论(0)
  • 2021-01-23 08:32

    It wouldn't work as you think, since you have made the function func virtual. This means that even if you were to convert the pointer to Parent to a pointer to Child, the func() of that object would still be Parent::func().

    Now, you could theoretically do something like this:

    #include <iostream>
    
    class Parent
    {
    public:
            virtual void foo() { std::cout << "parent" << std::endl; }
    };
    
    class Child : public Parent
    {
    public:
            virtual void foo() { std::cout << "child" << std::endl; }
    };
    
    int main()
    {
            Child child;
            child.foo(); // "child"
            child.Parent::foo(); // "parent"
            Parent parent;
            parent.foo(); // "parent"
            ((Child*)(&parent))->foo(); // still "parent"
            ((Child*)(&parent))->Child::foo(); // "child"
            return 0;
    }
    

    And while i may receive some downvotes for posting this broken code, i think that it is necessary to show what is happening in this case. You would need to convert both, the pointer to the object, and then specify exactly which function you are intending to call.

    Depending upon what you are doing, it may better be accomplished by using friend classes:

    #include <iostream>
    
    class ParentHelper;
    class ChildHelper;
    class Parent
    {
        friend class ParentHelper;
        friend class ChildHelper;
    private:
        int a=5;
    };
    
    class ParentHelper
    {
    public:
        virtual void func(Parent *p)
        {
            std::cout << "parent helper, but i see a " << p->a << std::endl;
        }
    };
    
    class ChildHelper : public ParentHelper
    {
    public:
        virtual void func(Parent *p)
        {
            std::cout << "child helper, but i see a also " << p->a << std::endl;
        }
    };
    
    void foo(Parent* p, ParentHelper *h)
    {
        h->func(p);
    }
    
    int main()
    {
        Parent p;
        ParentHelper ph;
        ChildHelper ch;
    
        ph.func(&p);
        ch.func(&p);
    
        foo(&p, &ph);
        foo(&p, &ch);
    
        return 0;
    }
    

    Note several things:

    1. Friendships are not inherited, so you must then list all children to ParentHelper that you intend to use.
    2. It does, however, give you a way to access all the data members of your Parent class as is, it won't cause some weird behaviour.
    3. This may still not be what you are looking for, but from your question i think that it may help.
    0 讨论(0)
提交回复
热议问题