联合中的c++继承示例

rjee0c15  于 2022-12-24  发布在  其他
关注(0)|答案(1)|浏览(137)

我有一个优化问题,我的程序经历了一个配置阶段,然后是执行阶段,在配置过程中,选择了一个函数的实现,然后在执行过程中的每个循环迭代中调用这个函数。
我想避免在每个循环中通过switch/case来检查调用哪个函数。解决方案是一个函数指针。现在这些函数中的每一个都可以有一个内部状态。一个好的解决方案是继承,其中每个类实现基类的"do_something()"函数,编译器生成一个vtable,一切都很好。
现在,我还想优化内存使用,因为我总是一次使用一个实现,每个示例的内部状态可以共享相同的内存空间,这成为一个问题,因为我不能把继承的示例放入一个联合体中;编译器对此并不满意(我想这是有道理的,因为可能是vtable)。
我发现这个问题的最佳解决方案是声明一个使用类外联合的数据结构,并将指向它的指针传递给类的每个示例。
有更好的办法吗?

    • EDIT**:在这种情况下,不使用动态分配。

参见以下代码:

    • 问题是**
#include <stdio.h>

using namespace std;

class Base{
    public:

    virtual void doprint() = 0;
};

class ChildA : public Base{
    public:
    virtual void doprint(){
        printf("I am A : %d",foo);
    };

    int foo;
};

class ChildB : public Base{
    public:
    virtual void doprint(){
        printf("I am B : %u", bar);
    };

    unsigned int bar;
};

int main()
{
    // Changing this for a struct works
    union{
        ChildA a;
        ChildB b;
    } u;

    // Configure phase
    u.a.foo = -10;
    Base *pbase = &u.a;
    
    // Exec phase
    pbase->doprint();
    
    return 0;
}

上面的代码让编译器说:第一个月
"丑陋的解决方案"

#include <stdio.h>

using namespace std;

union InternalData{
    struct {
        int foo;
    } data_for_a;
    struct {
        unsigned int bar;
    } data_for_b;
};

class Base{
    public:
    void init(InternalData *data)
    {
        m_data = data;
    }
    virtual void doprint() = 0;
    protected:
    InternalData* m_data;
};

class ChildA : public Base{
    public:
    virtual void doprint(){
        printf("I am A : %d", m_data->data_for_a.foo);
    };
    
};

class ChildB : public Base{
    public:
    virtual void doprint(){
        printf("I am B : %u", m_data->data_for_b.bar);
    };
};

int main()
{

    ChildA a;
    ChildB b;
    InternalData internal_data;
    
    // Configure phase
    internal_data.data_for_a.foo = -10;
    a.init(&internal_data);
    Base *pbase = &a;
    
    // Exec phase
    pbase->doprint();
    
    return 0;
}
goucqfw6

goucqfw61#

你并不是真的想要一个union(没有人真的想要一个联合体...)你真正想要的是一个放置你的实现的地方,一个接口指针,以及一个保证它在之后被正确地销毁的地方。

std::variant< ChildA, ChildB> impl; //define buffer that correctly destroys
Base *pbase; //declare the Base pointer.

impl = ChildA{}; //assign it an implementation
pbase = std::get<ChildA>(impl); //assign pointer to that implementation

如果你的C++没有std::variant,那么你可以自己实现析构缓冲区的关键部分,最小版本如下所示:

template<class Base, std::size_t size, std::size_t align>
class buffer {
    static_assert(std::has_virtual_destructor_v<Base>);
    std::aligned_storage_t<size, align> rawbuffer;
    Base* pbase=0;
public:
    ~buffer() {if (pbase) pbase->~Base();};
    
    Base* get() {return pbase;}
    
    template<class T, class...Us>
    Base* construct(Us&&...vs) {
        static_assert(sizeof(T) <= sizeof(rawbuffer));
        assert(pbase == nullptr);
        pbase = new(reinterpret_cast<T*>(&rawbuffer)) T(std::forward<Us>(vs)...);
        return pbase;
    }
};

注意,您几乎肯定希望Base具有一个析构函数。

相关问题