在C ++中为类提供可访问的“名称”时,您如何“不重复自己”?

时间:2016-08-19 05:53:11

标签: c++ dry

请考虑以下事项:

class Base {
  public:
    virtual std::string getName() = 0;
    ...
};

class Derived1 : public Base {
  public:
    static std::string getClassName() { return("Derived1"); }
    std::string getName() { return("Derived1"); }
    ...
};

class Derived2 : public Base {
  public:
    static std::string getClassName() { return("Derived2"); }
    std::string getName() { return("Derived2"); }
    ...
};

这个想法是,如果您将派生类作为模板参数传递,那么您可以通过getClassName获取其类名,而如果您将其作为指向基类的指针传递,则可以通过getName获取名称。

我在这里似乎有很多类似的问题,但是他们似乎都在问“我如何使用静态虚拟”,“为什么不存在静态虚拟”以及类似的各种东西,以及答案似乎解决了比我认为真正的潜在问题更多的问题,即:我怎样才能避免不得不重复自己使用该代码并在尽可能少的样板文件中提及名称两次? (不要重复自己,或干燥规则)

我也不想要宏。

4 个答案:

答案 0 :(得分:4)

首先,您可以在getClassName中重复使用getName

class Derived1 : public Base {
  public:
    static std::string getClassName() { return("Derived1"); }
    std::string getName() override { return getClassName(); }
    ...
};

现在,getName()的所有定义都是相同的,因此您可以将它们放在宏中以节省打字(并使它们更适合未来):

#define GET_NAME() std::string getName() override { return getClassName(); }

class Derived1 : public Base {
  public:
    static std::string getClassName() { return("Derived1"); }
    GET_NAME()
    ...
};

或者您也可以将getClassName捆绑在那里:

#define GET_NAME(maName) \
  static std::string getClassName() { return(maName); } \
  std::string getName() override { return getClassName(); }

class Derived1 : public Base {
  public:
    GET_NAME("Derived1")
    ...
};

你说"我也不想要一个宏,"但宏是一个很好的工具,我不会看到像这样使用它们的单一问题。但是,如果这不是你想要的,你也可以不用它们来做:

template <class Self>
struct GetName : public Base
{
  std::string getName() override { return Self::getClassName(); }
};

class Derived1 : public GetName<Derived1> {
  public:
    static std::string getClassName() { return("Derived1"); }
    ...
};

class Derived2 : public GetName<Derived2> {
  public:
    static std::string getClassName() { return("Derived2"); }
    ...
};

答案 1 :(得分:3)

不要害怕数据:

class Base {
  public:
    std::string const Name;
    Base(std::string Name) : Name(Name) { }

};

class Derived1 : public Base {
  public:
    static const std::string Name;
    Derived1() : Base { Name } { }
};

const std::string Derived1::Name { "Derived1" }

答案 2 :(得分:1)

创建一个单独的基类,它负责提供类名字符串:

class FakeRTTI
{
    std::string class_name;
public:
    FakeRTTI( std::string className ) : class_name(className) {}
    getClassName() { return class_name; }
}

然后,您可以在所有需要伪造,低效,显式,基于字符串的RTTI的类中执行此操作:

class Bla : public FakeRTTI
{
public:
    Bla() : FakeRTTI("Bla") {}
}

Pro的:

  • DRY:在构造函数中只有一个字符串“Bla”使用。
  • 单一责任原则
  • 没有虚拟函数调用

缺点:

  • 多重继承(这是con,真的吗?)
  • 您没有使用高效,标准,基于C ++的RTTI。
  • 你还在使用RTTI(摆脱它可能是不可行的,但这是代码闻到所有气味的迹象)。

答案 3 :(得分:0)

另一种可能的解决方案使用特征和类型擦除,如以下示例所示:

some_queryset.filter(pk=entry.pk).exists()

优点:

  • 名称不再是该类的一部分,您可以轻松地为一个类族定义一个共同特征(对所有类使用相同的标记)

  • 您没有任何虚拟方法

  • 您不需要两种方法可以做几乎相同的事情

缺点:

  • 您必须明确指定包含构造期间要使用的类型的标记