【问题标题】:Recursive Template idiom how to avoid that the base class is friend of the child classes递归模板习语如何避免基类是子类的朋友
【发布时间】:2012-10-26 06:58:00
【问题描述】:

我使用recursive template idiom 在工厂中自动注册基类的所有子类。但是,在我的设计中,子类必须将基类作为朋友类。由于我的基类的构造函数应该是私有的,以避免通过工厂以外的方式实例化此类。

总体目标是工厂的注册在 BaseSolver 中完成,并且 ChildClasses 不能通过工厂以外的方式实例化。

这是我的基类的代码,它自动在 SolverFactory 中注册所有子类。

template<class T>
struct BaseSolver: AbstractSolver
{
protected:
    BaseSolver()
    {
        reg=reg;//force specialization
    }
    virtual ~BaseSolver(){}
    /**
     * create Static constructor.
     */
    static AbstractSolver* create()
    {
        return new T;
    }

    static bool reg;
    /**
     * init Registers the class in the Solver Factory
     */
    static bool init()
    {
        SolverFactory::instance().registerType(T::name, BaseSolver::create);
        return true;
    }
};

template<class T>
bool BaseSolver<T>::reg = BaseSolver<T>::init();

这里是我的子类的头文件:

class SolverD2Q5 : public BaseSolver<SolverD2Q5>{

private:
  //how can I avoid this?
  friend class BaseSolver;

  SolverD2Q5();

  static const std::string name;

}

这很好用。但是我真的不喜欢将 BaseSolver 添加为友元类,但是我希望构造函数和静态成员名称是公开的。

是否有更优雅的解决方案或更好的布局来避免这种情况?

【问题讨论】:

  • 为什么 BaseSolver 需要成为朋友类?你能把 friend class T 添加到 BaseSolver 吗?
  • 这应该如何帮助我不想访问 BaseSolver 中的私有方法,但 BaseSolver 应该访问 Solver 的私有成员。
  • 我喜欢这种设计。为什么friend 声明是个问题?
  • private 替换为SolverD2Q5 中的protected 仍然不公开,因此将BaseSolver 中的T 替换为派生自@987654331 的包装类@解决问题。我之前的回答只是给了一个提示。现在我添加了一个完整的例子。我看不出它不是一个解决方案,甚至不是侵入性的。而且它不会在可执行文件中创建额外的代码。

标签: c++ templates design-patterns idioms crtp


【解决方案1】:

更新: 我相信这个技巧还没有被理解,因此我现在创建了完整的解决方案。这只是对 OP 代码的一点点改动。只需将 BaseSolver 中的 T 替换为派生自 T 的空类定义即可。

原文: 我认为您可以通过将友谊委托给基础 Solver 私有的包装类来做到这一点。该类将从要为其创建实例的任何类继承。编译器应该优化包装类。

#include <iostream>
#include <map>

struct AbstractSolver { virtual double solve() = 0; };

class SolverFactory
{
    std::map<char const * const, AbstractSolver * (*)()> creators;
    std::map<char const * const, AbstractSolver *> solvers;
public:
    static SolverFactory & instance()
    {
        static SolverFactory x;
        return x;
    }
    void registerType(char const * const name, AbstractSolver *(*create)())
    {
        creators[name] = create;
    }
    AbstractSolver * getSolver(char const * const name)
    {
        auto x = solvers.find(name);
        if (x == solvers.end())
        {
            auto solver = creators[name]();
            solvers[name] = solver;
            return solver;
        }
        else
        {
            return x->second;
        }
    }
};

template<class T> class BaseSolver : public AbstractSolver
{
    struct Wrapper : public T { // This wrapper makes the difference
        static char const * const get_name() { return T::name; }
    };
protected:
    static bool reg;
    BaseSolver() {
        reg = reg;
    }
    virtual ~BaseSolver() {}
    static T * create() {
        return new Wrapper; // Instantiating wrapper instead of T
    }
    static bool init()
    {
        SolverFactory::instance().registerType(Wrapper::get_name(), (AbstractSolver * (*)())BaseSolver::create);
        return true;
    }
};

template<class T>
bool BaseSolver<T>::reg = BaseSolver<T>::init();

struct SolverD2Q5 : public BaseSolver<SolverD2Q5>
{
public:   
    double solve() { return 1.1; }
protected: 
    SolverD2Q5() {} // replaced private with protected
    static char const * const name;
};
char const * const SolverD2Q5::name = "SolverD2Q5";

struct SolverX : public BaseSolver<SolverX>
{
public:    
    double solve() { return 2.2; }
protected: 
    SolverX() {} // replaced private with protected
    static char const * const name;
};
char const * const SolverX::name = "SolverX";

int main()
{
    std::cout << SolverFactory::instance().getSolver("SolverD2Q5")->solve() << std::endl;
    std::cout << SolverFactory::instance().getSolver("SolverX")->solve() << std::endl;
    std::cout << SolverFactory::instance().getSolver("SolverD2Q5")->solve() << std::endl;
    std::cout << SolverFactory::instance().getSolver("SolverX")->solve() << std::endl;

    char x;
    std::cin >> x;
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 1970-01-01
    • 2014-09-10
    • 2011-10-14
    相关资源
    最近更新 更多