C++:实现从基类指针中获取子类的成员

看见标题...可能会觉得很简单...不就是一个static_cast<>嘛!

但是如果我不通过子类的类型呢?那么static_cast就失效了;

先卖个关子,看一下最终的调用形式:

形式:
Type* ptr = ClassManger::GetComponent<Type>(Base* p);

例如:

class Base{
}
class derive :public Base{
    int a = 10086;
}
int main(){
    Base* ptr = new derive;
    int *component = ClassManger::GetComponent<int>(ptr);
    std::cout<<*component<<std::endl;
}

学过虚幻引擎的人可能会比较眼熟,这也是我想着写一个这样的demo的原因,因为我最近在写一个项目,后期可能会用到这样的功能,所以实现一下[从零到有,搞了一天...大量的精神攻击....!];

秉着实践中来的原则,先将一下这样的功能的应用场景:

应用场景:

比如你在开发一款游戏,character会一定范围内实现持有某个接口指针的实例产生互动。

首先通过一定的算法,获取到这个范围内可获取的物品,然后利用GetComponent函数来获取这些物品的这个组件,如果物品没有该组件,那么就返回nullptr,如果有,那么就返回该物品组件的指针;

想一下这个范围内的所有物品肯定是不同类的实例,那么肯定是不能用static_cast这种硬编码的手段了;

好,知道了应用场景,那么接下来就是然后实现了

实现:

首先从信息的角度出发,我们调用GetComponent方法时,提供了那些信息?

1:基类指针;

2:组件类型;

光这些信息肯定是不够的,因为我们最终是需要知道基类指针真正指向的子类是什么,还需要知道该组件在子类中的位置,有了这些信息最终就能实现该功能;

问题1:

第一个:我们如何知道基类指针指向的子类是什么?

这个其实很简单,我们只需要用一个全局的子类就行,即所有的该功能能作用的对象都应该派生自改类;

然后该基类就能通过多态的手段获取到不同派生类的标识码了;

标识码的话,我们可以用typeid(type)关键字,该关键字返回的对象可以过去type的唯一hashCode;

这个问题我们解决了!;

问题2:

我们怎么知道该数据成员中类中的位置,即我们怎么通过提供的组件类型单位到类中对应的成员呢?

这个的话,需要用到类数据成员指针了;

实现我们要预处理能被定位到的数据成员的数据成员指针,即在调用该功能时需要对所有继承体系中的类进行预处理,才能获得该信息;

我实现的形式呢,是下面这样的:

    Class::Register()
    ->AddMemberVar(&Class::number1)
    ->AddMemberVar(&Class::number2);

Class是继承体系中的类[继承了全局基类的派生类],该类需要实现一个静态方法Register,该方法返回一个构建存储信息类的builder类;,然后调用该builder类的Add方法将需要单位功能的数据成员的数据成员指针加入其中进行预处理;

那我们怎么预处理呢?

预处理数据成员指针;

先看一下上面的形式,我们通过的是那些信息,首先,一个数据成员指针包含那些信息?2个,一个是该成员所属的类,第二个是该数据成员自身的变量类型,看AddMemberVar函数的声明:

template<typename Tyep,class Class>
std::unique<builder> AddMemberVar(Type Class::* var);

看,我们通过模板,获取到了这两个信息,然后呢,关键来了,我们需要将这两个信息封装到一个lambda表达式中,最终存储到一个std::function<void*(void*)>对象中,这是最关键的一部分;

这个std::function<void*(void*)>函数的输入输出是什么呢?

输入:基类指针;

输出:成员变量指针;

看信息的转变都在这完成了;

解释不如看代码:

class MemberVar {
public:
    template<typename Type, typename Class>
    MemberVar(Type Class::* var) {
        getter = [var](void* obj)->void* {
            return &((static_cast<Class*>(obj))->*var);
        };
    }
private:
    std::function<void*(void*)> getter;
};

到这,只能意会了;

我对此的理解是:类信息通过函数模板存储到一个lambda表达式中,因为函数的特性是主要函数特征相同,那么就能用同一个类型的函数指针存储,我将这个过程称为信息的" 压缩 "和" 解压 "和" 预处理 ",整个实现都是围绕着这个思想实现的,我们先将基类指针压缩成void*的指针,虽然我们对该基类指针的信息都丧失了,但是我们有预处理的信息对其进行解压,然后解压出新的信息;

对其大概的描述就是:基类指针->void*->子类指针->成员变量指针->void*->成员变量指针;

下面是全部源码,因为是一个小demo,所以代码量有点小:

时间复杂度的话,o(1)

#include<memory>
#include"iostream"
#include<string>
#include<functional>
#include<type_traits>
#include<unordered_map>
#include<atomic>
#include<any>

class LCBaseObject {
public:
    virtual size_t GetHashCode() = 0;
    template<typename T>
    static size_t GetStaticHashCode() { return typeid(T).hash_code(); }
};

template<typename Class>
class TypeDiscriptorBuilder;


class MemberVar {
public:
    template<typename Type, typename Class>
    MemberVar(Type Class::* var) {
        hashCode = typeid(Type).hash_code();
        getter = [var](void* obj)->void* {
            return &((static_cast<Class*>(obj))->*var);
        };
    }
    std::function<void*(void*)> GetGetter() { return getter; }
    size_t GetHashCode() { return hashCode; }
private:
    size_t hashCode;
    std::function<void*(void*)> getter;
};

class TypeDiscriptor {
public:
    std::unordered_map<size_t, std::unique_ptr<MemberVar>> hashMap;
    template<typename Class>
    friend class TypeDiscriptorBuilder;
};

template<typename Class>
class TypeDiscriptorBuilder {
public:
    TypeDiscriptorBuilder(std::shared_ptr<TypeDiscriptor>& pushIn)
    :pushIn(pushIn){
        result = std::make_shared<TypeDiscriptor>();
    }
    template<typename Type>
    TypeDiscriptorBuilder<Class>* AddMemberVar(Type Class::* var) {
        std::unique_ptr<MemberVar> registerVar = std::make_unique<MemberVar>(var);
        result->hashMap[registerVar->GetHashCode()] = std::move(registerVar);
        return this;
    }
    ~TypeDiscriptorBuilder() {
        std::cout << 1;
        pushIn = std::move(result);
    }
private:
    std::shared_ptr<TypeDiscriptor>& pushIn;
    std::shared_ptr<TypeDiscriptor> result;
};

class ClassNode {
public:
    ClassNode(size_t hashCode)
        :hashCode(hashCode) {}
    size_t hashCode;
    std::shared_ptr<TypeDiscriptor> disc;
};

//全局子类管理器,基于typeid
class SubClassManger {
public:
    static SubClassManger& Get() {
        static SubClassManger single;
        return single;
    }
    template<typename T>
    std::shared_ptr<TypeDiscriptorBuilder<T>> AddClass(size_t hashCode);
    std::unordered_map<size_t, std::shared_ptr<ClassNode>>& GetMap() { return classMap; }
    template<typename Type>
    Type* GetComponent(LCBaseObject* obj);
private:
    SubClassManger() {}
    std::unordered_map<size_t, std::shared_ptr<ClassNode>> classMap;
};

template<typename T>
std::shared_ptr<TypeDiscriptorBuilder<T>> SubClassManger::AddClass(size_t hashCode) {
    std::shared_ptr<TypeDiscriptor> disc;
    std::shared_ptr<ClassNode> node = std::make_shared<ClassNode>(hashCode);

    classMap[hashCode] = std::move(node);
    std::shared_ptr<TypeDiscriptorBuilder<T>> builder = std::make_shared<TypeDiscriptorBuilder<T>>(classMap[hashCode]->disc);
    return builder;
}


template<typename Type>
Type* SubClassManger::GetComponent(LCBaseObject* obj) {
    size_t hashCode = obj->GetHashCode();
    //该类没有登记
    if (classMap.find(obj->GetHashCode()) == classMap.end())
        return nullptr;
    std::function<void*(void*)> getter;
    if (classMap[obj->GetHashCode()]->disc->hashMap.find(LCBaseObject::GetStaticHashCode<Type>()) ==
        classMap[obj->GetHashCode()]->disc->hashMap.end()) {
        return nullptr;
    }
    getter = classMap[obj->GetHashCode()]->disc->hashMap[LCBaseObject::GetStaticHashCode<Type>()]->GetGetter();
    void* result = getter(obj);
    return static_cast<Type*>(result);
}

class TestClass :public LCBaseObject{
public:
    size_t GetHashCode() override { return typeid(TestClass).hash_code(); }

    std::string b;
    std::string a = "win";
    std::function<void(void)> fun;
    static std::atomic<bool> IsRegister;
    static std::shared_ptr<TypeDiscriptorBuilder<TestClass>> Register() {
        if (IsRegister) return nullptr;
        IsRegister = true;
        if (std::is_base_of<LCBaseObject, TestClass>::value == false) {
            //断言!
        }
        return SubClassManger::Get().AddClass<TestClass>(LCBaseObject::GetStaticHashCode<TestClass>());
    }
};

void printWin() {
    std::cout << "i am cpper" << std::endl;
}

std::atomic<bool> TestClass::IsRegister;

int main() {
    TestClass::Register()->AddMemberVar(&TestClass::a)->AddMemberVar(&TestClass::fun);
    TestClass instance;
    instance.fun = printWin;
    std::string* win = SubClassManger::Get().GetComponent<std::string>(&instance);
    std::function<void(void)>* fun = SubClassManger::Get().GetComponent<std::function<void(void)>>(&instance);
    std::cout << *win << std::endl;
    (*fun)();
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值