Pergunta

So let's say I have two classes that inherit a base class that has a pure virtual function. Both of the classes implement their own version of that function, but don't add additional member variables so they have the same size. Now sometimes, in the middle of the execution of the program, I want to convert one class to the other without copying all its data. So basically I want to make it use the virtual table of the other class. Is there a portable way of doing this?

Foi útil?

Solução

The portable way to do this would be to implement your own class system that actually has virtual-pointers that can be copied.

There is no such thing as a virtual-pointer in standard C++.

Outras dicas

Nope. As far as the language is concerened, there is no such thing as a virtual table, let alone rules about how it looks/what it contains/where it is stored.

Some form of composition is probably more appropriate to your task.

One young colleague at Andersen Consulting (now Accenture) in Norway once approached me with a serious problem. Their applications, developed in Visual Basic, took a heck of a long time to load. He suspected that this might be because they put each class in its own DLL?

Fearing the worst, I inquired further. And yes, they also had problems with arbitrary crashes etc.

He suspected that the otherwise inexplicable crashes might be connected to their ingenious scheme for changing the type of an object at runtime, by replacing the vtable pointer?

I suggested that maybe they shouldn't really do these things. He looked skeptically at me, and ventured that they didn't have time to do things from scratch again. In fact, they were already stretching it, and there were various problems with that, like their project leader insisting that they work at the client site instead of participating at obligatory meetings. To me, that sounded like mushroom management (keep them in the dark, when a head pops up, cut it): these things often go together.

Anyway, I give you the same advice: don't.

Perhaps you can instead implement fast move operations to move data from a to b?

Or, perhaps you will discover that it's all a case of premature optimization?

Cheers & hth.,

Is there a portable way of doing this?

Absolutely not. The specifics of how virtual functions are implemented are not defined by the spec, and therefore there is no portable way to pretend one virtual class is another.

As the other answers have said, actually changing the vtable is definitely non-portable.

However, there are several work-arounds that could let you accomplish similar semantics without actually changing the class type:

This simplest solution would be to "roll your own" inheritance with an enum describing the current implementation:

class MyClass
{
    public:
    enum DerivedType { A, B };

    private:
    DerivedType myType;

    public:
    void myVirtualFunction()
    {
        if (myType == A)
            myAFunction();
        else
            myBFunction();
    }
}

You could also use a function pointer as a public member variable, which is set to the function indicating the class type. Then you could set the function pointer to the other class's function to "change it's type"

Since you mention you want to avoid copying the data, you could keep your different classes, but have reference-counting pointers to all your member variables, so that you can create new objects of the opposite type from each other quickly.

What about using placement new? This is maybe not quite portable, but it does exactly the required thing - replaces vtable, and nothing more. Just need to take care of constructor - use an empty one.

struct Base
{
    int someData;
    virtual int GetValue() = 0;
};

struct A : public Base
{
    int GetValue() override { return 11111; }
};

struct B : public Base
{
    int GetValue() override { return 22222; }
};

A ob;
ob.someData = 123;
auto ob2 = new (&ob) B;
auto value = ob2->GetValue();

Not mentioning obvious things like classes size, best practices, etc.

Even though this question is old, I would like to bring up a way to do this. (Not quite sure on the portability)

From what I understand you have a class B and C that inherit from some class A and only a single virtual function exists between them. (The method I present here works if B and C are not related as well.)

class A {
public:
    virtual std::string hello() = 0;
};

class B : public A { 
public:
    virtual std::string hello() { return "B"; }
};

class C : public A {
public:
    virtual std::string hello() { return "C"; }
};

And then you want to take a B to a C then call hello and get "B".


So, there is a way to create a watered down version of the boost::any that will cast anything to anything as long as it fits:)

struct parent {};

template< typename T >
struct child : public parent {
    child(T const& t): item(t){}
    mutable T item;
};

template< typename T >
T& as(parent const & p) { return static_cast< child< T > const& >(p).item; }

Then mix it all together:

B b;
parent* p = new child< B >(b);
std::cout << as< C >(*p).hello() << std::endl;
// ==== OUTPUT ====
// B

Can see the code in action here.


To go a step further we can create a function that converts from one type to another without giving a gnat's rear-end about what goes on between them.

template< typename TO, typename FROM >
TO& convert(FROM const& from) {
    parent* p = new child< FROM >(from);
    return as< TO >(p);
};

This can be ran here.

(Realized that I missed the inheritance in these example code links, but after reading the question I think that is what was actually desired. So, to see the test without inheritance go here)


Some other code that I started playing with that I thought might help some as well...

#include <iostream>
#include <string>

class B {
public:
    virtual char hello() {return 'B';}
};

class C {
public:
    virtual int hello() {return 65;}
};

struct parent {};

template< typename T >
struct child : public parent {
    child(T const& t): item(t){}
    mutable T item;
};

template< typename T >
T& as(parent const & p) { return static_cast< child< T > const& >(p).item; }

template< typename TO, typename FROM >
TO& convert(FROM const& from) {
    parent* p = new child< FROM >(from);
    return as< TO >(*p);
};

int main()
{
    B b;
    std::cout << convert< C, B >(b).hello() << std::endl;
    C c;
    std::cout << convert< B, C >(c).hello() << std::endl;
}
// ==== OUTPUT ====
// 66
// A

Figured out how to make it all within the convert function:

template< typename TO, typename FROM >
TO& convert(FROM const& from) {
    struct parent {};

    struct child : public parent {
        child(FROM const& t): item(t){}
        mutable FROM item;
    };

    struct sibling : public parent {
        sibling(TO const& t): item(t){}
        mutable TO item;
    };

    parent* p = new child(from);
    return static_cast< sibling const& >(*p).item;
};
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top