C++ 中的流利接口和继承

Posted

技术标签:

【中文标题】C++ 中的流利接口和继承【英文标题】:Fluent interfaces and inheritance in C++ 【发布时间】:2009-06-24 22:28:30 【问题描述】:

我想构建一个具有一些通用功能和流畅接口的基础(抽象)类(我们称之为type::base),我面临的问题是所有这些方法的返回类型

  class base 
    public:
       base();
       virtual ~base();

       base& with_foo();
       base& with_bar();
    protected:
       // whatever...
  ;

现在我可以制作子类型了,例如:

  class my_type : public base 
    public:
      myType();        
      // more methods...
  ;

使用这样的子类型时会出现问题:

 my_type build_my_type()
 
    return my_type().with_foo().with_bar();
 

这不会编译,因为我们返回的是 base 而不是 my_type。

我知道我可以:

 my_type build_my_type()
 
    my_type ret;
    ret.with_foo().with_bar();

    return ret;
 

但我在想如何实现它,我没有找到任何有效的想法,一些建议?

【问题讨论】:

我已经删除了命名空间的东西,因为它与问题无关(据我所知) 【参考方案1】:

这个“丢失类型”的问题可以通过模板来解决——但是相当复杂。

例如。

class Pizza

  string topping;
public:
  virtual double price() const;
;

template <class T, class Base>
class FluentPizza : public Base

  T* withAnchovies()  ... some implementation ... ;
;

class RectPizza : public FluentPizza<RectPizza, Pizza>

  double price() const  return length*width; :) 
;

class SquarePizza : public FluentPizza<SquarePizza, RectPizza>

   ... something else ...
;

然后你就可以写了

SquarePizza* p=(new SquarePizza)->withAnchovies();

模式是,而不是

class T : public B

你写

class T : public Fluent<T, B>

另一种方法是不在对象上使用流式接口,而是在指针上使用:

class Pizza  ... ;
class RectPizza  ... ;
class SquarePizza  ... whatever you might imagine ... ;

template <class T>
class FluentPizzaPtr

  T* pizza;
public:
  FluentPizzaPtr withAnchovies() 
    pizza->addAnchovies(); // a nonfluent method
    return *this;
  
;

这样使用:

FluentPizzaPtr<SquarePizza> squarePizzaFactory()  ... 

FluentPizzaPtr<SquarePizza> myPizza=squarePizzaFactory().withAnchovies();

【讨论】:

您能举个例子吗?这可能很有趣。 参见。编辑。这可能很有趣,我不知道我是否会亲自使用它... 所以你实际上切换到指针。然后你可以只使用简单的多态性,不使用 CRTP,并返回 base*。 你可以想象那里的引用(如果你不想要指针)。我只是不会让这些方法总是返回一个副本,但这不是核心问题。 CRTP 的主要目的是消除对 base(或 base* 或 base&)的衰减,这是我认为您希望避免的。 如果您要返回指针或引用,请返回 static_cast(this) 或 static_cast(this) (如果您不这样做,后者也适用于副本不介意更改原件。如果您想退回副本并保持原件不变,请执行“T ret=*(T)this; do_something_with_ret; return ret;”之类的操作。【参考方案2】:

您应该返回引用/指针,并且您不需要保留类型信息。

class base 
  public:
     base();
     virtual ~base();

     base &with_foo();
     base &with_bar();
  protected:
     // whatever...
;

class my_type : public base 
  public:
    my_type();        
    // more methods...
;

base *build_my_type()

   return &new my_type()->with_foo().with_bar();

您已经有一个虚拟析构函数。大概您还有其他虚拟功能。通过基类型和在那里声明的虚函数访问所有内容。

【讨论】:

问题是我不想丢失类型。【参考方案3】:

一个解决方案可以这样工作:

return *static_cast<my_type*>(&my_type().with_foo().with_bar());

使用static_cast 基本上告诉编译器'我知道我在这里做什么'。

【讨论】:

【参考方案4】:

在 C++ 中,您应该返回指针或引用而不是值。此外,您可能想解释一下“流畅的界面”是什么意思。

【讨论】:

抱歉——我一看到“Martin”这个名字,我的bullsh*t 检测器就以最大音量熄灭,我无法再阅读了。无论是名字还是姓氏,似乎都没有关系。【参考方案5】:

我在 C# 中执行此操作的方式,并且我相信它也可以在 C++ 中工作,是为 with_foo()with_bar() 提供默认实现...原谅我的 c#,但是:

class base 
  virtual base with_foo()
   throw new NotImplementedException(); 
  virtual base with_bar();
   throw new NotImplementedException(); 

【讨论】:

但这并不能解决问题 - 问题是类型不匹配,与问题无关。

以上是关于C++ 中的流利接口和继承的主要内容,如果未能解决你的问题,请参考以下文章

C++ 中的类接口继承

C++中的接口继承

C++ 继承、接口

C++中的多重继承

C++ 中的继承默认访问修饰符

C++多态