禁止转换为父级,但公开父级接口(设计问题)

Posted

技术标签:

【中文标题】禁止转换为父级,但公开父级接口(设计问题)【英文标题】:Forbid conversions to parent, but expose publicly parent's interface (design issue) 【发布时间】:2018-11-06 19:05:12 【问题描述】:

我不确定如何以最优雅的方式实现(用 C++ 表示)以下问题。假设我们有一个库提供类PerfectCounter 具有丰富的非虚拟接口。我想实现继承自PerfectCounter 的类DisturbedCounter。新类应公开与其父级相同的接口,例如允许为两个类的实例对调用一些运算符(<>、== 等)。此外,我想禁止两个类之间的转换(两种方式)。

DisturbedCounter 可以从PerfectCounter 私有继承。它将阻止从DisturbedCounterPerfectCounter 的转换。但是,我必须在 DisturbedCounter 中明确地将 99% 的 PerfectCounter API 重新声明为“公共”。这意味着未来需要大量的编写和维护。

有没有更好的方法来解决这个问题?

我可以使用 C++17。

【问题讨论】:

如果这些函数是虚拟的,为什么不希望转换为父函数?如果它们不是虚拟的,那么覆盖它们似乎是个坏主意。 @SergeyA PerfectCounter 有非虚拟界面,之前没提过。 好问题,唉,在今天的 C++ 中没有答案。我们希望有using A::* except blargh; 也许有一天。 @n.m.确实,这正是我正在寻找的东西。我害羞地希望有一个简洁的 C++1* 结构可以解决我的问题。 【参考方案1】:

您可以让 DisturbedCounter 私下从 PerfectCounter 继承。那么它们之间的转换将是不允许的(因为在这种情况下 DisturbedCounter 不是 PerfectCounter,它只是根据一个实现)。

然后您可以使用using 指令公开您希望在 DisturbedCounter 中公开使用的 PerfectCounters 接口部分,并添加您想要的任何比较运算符的实现。

当然还有其他方法可以解决,但这至少是您要考虑的一种。

【讨论】:

这是我在第二段中描述的方法(可能我不够清楚)。这是一个相当乏味的解决方案,因为我必须为 PerfectCounter 中的每个方法使用using @Goofy 我现在看到了(重读时)——我想我读起来很烂。反正;是的,很乏味,但仍然是能想到的解决这个特定问题的最直接的方法。【参考方案2】:

我不确定这是否正是您需要的,因为 DisturbedCounter 不是从 PerfectCounter 继承的。它使用CRTP 来避免转换,并且它应该允许您只执行每个方法一次(除了那些真正应该不同的方法)并且您不需要来自基类的using。我不擅长模板,所以这可能会被某人撕开,但是o.t.o.h.,如果不是,它可能会给你一些想法,它可能是一些可以构建的东西。缺少很多样板。

#include <iostream>

template<class T>
struct Common 
protected:
    double m_count;

public:
    Common() : m_count(0) 
    Common& operator=(const Common& rhs) 
        m_count = rhs.m_count;
        return *this;
    

    operator double () const  return m_count; 
    double get() const  return m_count; 
;

// type specific implementation
struct PerfectCounter : Common<PerfectCounter> 
    void count() 
        m_count += 10.0;
    
;

// type specific implementation
struct DisturbedCounter : Common<DisturbedCounter> 
    void count() 
        m_count += 9.9;
    
;

int main() 
    PerfectCounter a;
    a.count();
    DisturbedCounter b;
    b.count();

    //a = b; // error
    //b = a; // error

    if( a>b ) std::cout << "a>b\n";

    std::cout << a << " " << b << "\n";

输出

a>b
10 9.9

【讨论】:

以上是关于禁止转换为父级,但公开父级接口(设计问题)的主要内容,如果未能解决你的问题,请参考以下文章

HTML5 画布调整为父级

PyQt 多线程,无法为父级创建子级

为父级调用渲染的主干子视图

QObject 使用 setParent() 将 QList 设置为父级

如果它包含特定类的子级,则为父级设置样式[重复]

在 Laravel 中为父级和子级创建活动类