【问题标题】:C++: Compiling unused classesC++:编译未使用的类
【发布时间】:2013-07-04 09:16:47
【问题描述】:

我喜欢使用一种模式来实现工厂类,如下所示(摘自我对this 问题的回答):

class Factory
{
public:
    template<class DerivedType>
    DerivedType::CreatedType *createType()
    {
        DerivedType::CreatedType *r = (DerivedType::CreatedType) (*(m_creators[DerivedType::id]))();
        return r;
    }
protected:
    static std::map<int,void *(*)()> m_creators;
};

std::map<int,void *(*)()> Factory::m_creators = std::map<int,void*(*)()>();

template<class Derived, class CreatedType>
class CRTPFactory : public Factory
{
    typedef typename CreatedType CreatedType;
public:
    static bool register() 
    {
        Factory::m_creators.push_back(std::make_pair(Derived::id,Derived::create);
        return true;
    }

private:
    static bool m_temp;
};

template<class Derived>
bool CRTPFactory<Derived>::m_temp = CRTPFactory<Derived>::register();

class AFactory : public CRTPFactory<AFactory,A>
{
private:
    static A *create() 
    {
        //do all initialization stuff here
        return new A;
    }

public:
    static const int id = 0;
};

这允许为新类型扩展工厂,而无需更改工厂类。它还允许为不同类型实现特定的创建算法,而无需更改工厂类。但是,这种模式存在一个主要问题。 AFactory 类从不显式使用。它在加载时通​​过 CRTPFactory 的成员 temp 注册其创建者函数。这可能有点难以理解,但它非常易于使用。问题是 AFactory 没有被编译,所以它的静态参数在加载时没有被初始化。我的问题是,是否可以强制编译器(我使用的是 VS 2012,但 GCC 的答案也很好)编译 AFactory 而无需显式创建它的实例? 我在 VS 中使用的一个解决方案是 dllexport AFactory,这样编译器就会编译该类,即使它不知道有人实例化它。这是因为它假定其他一些 dll 可能会实例化它。这个解决方案的问题是工厂类必须像其他代码一样在单独的 dll 中实现。而且这在 GCC 上也不起作用。

【问题讨论】:

  • 对不起,我重命名了很多变量,所以在这里看起来不错。我忘了那个。
  • 这种技术还有一个问题,那就是它要求CRTPFactory 的所有特化在同一个翻译单元(又名 cpp 文件)中实例化,以保证它们在Factory::m_creators 的初始化。我建议使用不依赖于静态成员构造函数的执行顺序的技术。

标签: c++ templates design-patterns compiler-construction


【解决方案1】:

CRTPFactory&lt;AFactory,A&gt; 继承会导致类的隐式实例化,但不会导致其成员的定义。

[temp.inst]

类模板特化的隐式实例化导致类成员函数、成员类、静态数据成员和成员模板的声明而不是定义或默认参数的隐式实例化;

您可以简单地显式实例化m_temp 成员,而不是从CRTPFactory&lt;AFactory,A&gt; 继承。

template bool CRTPFactory<AFactory,A>::m_temp;

作为参考,这是修改后的示例(以可编译的形式):

#include <map>

class Factory
{
public:
    template<class DerivedType, class CreatedType>
    CreatedType *createType()
    {
        CreatedType *r = (CreatedType) (*(m_creators[DerivedType::id]))();
        return r;
    }
protected:
    static std::map<int,void *(*)()> m_creators;
};

std::map<int,void *(*)()> Factory::m_creators = std::map<int,void*(*)()>();

template<class Derived, class CreatedType>
class CRTPFactory : public Factory
{

public:
    static bool register_() 
    {
        Factory::m_creators.insert(std::make_pair(Derived::id,Derived::create));
        return true;
    }

  static bool m_temp;
};

template<class Derived, class CreatedType>
bool CRTPFactory<Derived, CreatedType>::m_temp = CRTPFactory<Derived, CreatedType>::register_();

struct A
{
};

class AFactory
{
public:
    static void *create() 
    {
        //do all initialization stuff here
        return new A;
    }

public:
    static const int id = 0;
};

template bool CRTPFactory<AFactory,A>::m_temp;

【讨论】:

    【解决方案2】:

    静态类成员应该在某处显式创建。

    在您选择的 cpp 文件中执行类似操作应该可以:

    int AFactory::id = 0
    

    【讨论】:

    • 是的,这就是我在 GCC 中工作时所做的事情,但我的工厂项目创建了数十个类,所以我有一个包含该行二十次的 CPP 文件。这既非常难看,也使得整个设计模式更难使用,因为如果编写工厂类的人不是使用它的人,你会感到很困惑。
    • 嗯,我现在明白你的问题了。 id 字段是否必须是工厂的静态类变量?是否可以将其移至您的工厂基类之一?
    【解决方案3】:

    你的假设是错误的。 class AFactory 肯定是编译的。可能有好几次,因为它在标题中。

    您真正的问题可能是class AFactory 没有注册。为什么会这样?哪个语句会导致它成为?每个语句最终都是从main() 或从全局变量的初始化程序调用的。

    【讨论】:

    • 它应该被注册,因为派生的静态变量 m_temp 应该在加载时初始化。
    • 如果我导出类然后 in 被注册。我认为唯一的区别是当我导出它被编译的类时。
    • @BenjyKessler:这是另一个不正确的假设。还有很多不同之处。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-06
    • 2017-08-06
    • 2022-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-18
    相关资源
    最近更新 更多