通过指向base,static_cast,crtp,删除模板的指针派生的成员

时间:2018-09-12 18:22:32

标签: c++ templates design-patterns polymorphism

寻找:从指向基址的指针访问派生类的成员。

荒谬的还原:

class Base
{
public:
    int member_of_base;
};

class Derived : public Base
{
public:
    int member_of_derived;
};

我当前正在使用模板:

template <class T>
class Client
{
    T* data; // T is Base or Derived
};

类层次结构中的构成层次很少,因此我必须在所有层次结构中携带模板类型参数。克服此问题的最佳方法是什么? 显然,我无法通过指向Base的指针访问Derived的成员,即:

Base* foo = new Derived();
foo->member_of_derived; // no go

因此,我正在使用:

Client<Base>
Client<Derived>

我正在尝试提出一种无需模板即可使用的解决方案。我知道可以使用的选项:

  • void * //根据需要放置旧的C并进行强制转换,它们都是指针 (如在内存地址中)
  • static_cast<Derived*>(pointer_to_base); //在编译时输入安全类型。
  • 用客户的模板方法包装演员表(不要在这里与设计模式混淆)

最后一个选项似乎是最“优雅”的,即:

template <class T>
T* get_data() const { return static_cast<T*>(data); }

但是,四处看看告诉我可能存在一种未知的方法。 我看到了CRTP,但是这使我回到了模板,这是我想要的原始东西。 实现这一目标的方法或流行方法是什么?

实际代码使用了shared_ptr,weak_ptr和enable_shared_from_this和weak_from_this。我正在寻找类型安全的“多态成员”访问权限。

编辑:它们不仅是“整数”。它们可以是完全不同的类型,例如base中的protobuf和派生中的Json :: Value。而且我正在尝试使用指向Base / Derived的指针,这将使我能够访问它们各自的成员。

1 个答案:

答案 0 :(得分:0)

虚拟吸气剂可以为您解决问题;由于数据类型不同,您可以将它们打包到std::variant中。

class Base
{
    // having private members probably is more appropriate
    int member_of_base;

public:
    using Data = std::variant<int, double>;

    virtual ~Base() { } // virtual functions -> have a virtual destructor!

    virtual Data getMember() // or just "member", if you prefer without prefix
    {
        return member_of_base;
    }
};

class Derived : public Base
{
    double member_of_derived;

public:   
    Data getMember() override
    {
        return member_of_derived;
    }
};

std::unique_ptr<Base> foo = new Base();
foo->getMember(); // member_of_base;
std::unique_ptr<Base> bar = new Derived();
bar->getMember(); // member_of_derived;

虽然std::variant是公认的,但还没有完全没有模板,但我认为以这种形式可以接受...

尽管有一些问题,

  1. 访问值并非最简单,您可以考虑使用visit函数。
  2. 更严重:基类(或您定义要使用的变体的任何其他地方)需要了解可能正在使用的所有类型,添加新类型将强制重新编译所有其他类。
  3. 它有不良设计的味道。为什么派生类必须返回与用于相同目的的基类不同的东西?

如果您可以将要完成的工作委托给类本身,则可以解决所有这些问题:

class Base
{
    int member_of_base;

public:
    virtual ~Base() { }

    virtual void doSomething()
    {
        /* use member_of_base */
    }
};

class Derived : public Base
{
    double member_of_derived;

public:
    void doSomething() override
    {
        /* use member_of_derived */   
    }
};

后者是真正的多态方法,通常是可行的方法;而上面的示例返回的是void,您可能只需要在基类和派生类中进行所有必要的计算,直到最终获得某种通用数据类型并返回该类型。示例:

class Base
{
    int64_t m_balance; // in 100th of currency in use

public:
    virtual ~Base() { }

    virtual int64_t balance()
    {
        return m_balance;
    }
};

class Derived : public Base
{
    long double m_balance; // arbitrary values in whole currency entities

public:
    int64_t balance() override
    {
        // calculate 100th of currency, correctly rounded:
        return std::llround(m_balance * 100);
    }
};

诚然,我怀疑以双倍(即使很长)表示一个平衡是一个好主意(带有四舍五入,精度等问题)...