【问题标题】:Redundant private member data replication in implementation hierarchy实现层次结构中的冗余私有成员数据复制
【发布时间】:2013-01-17 22:24:53
【问题描述】:

我的问题涉及公共继承和私有继承的组合作为在 C++ 类中分离接口和实现的工具。在这种模式中,接口基类声明了公共函数(class Base0)。通用实现是在从接口基 (class Impl0 : virtual public Base0) 虚拟派生的类中执行的。此类包含任何公共数据成员。扩展类分两步编写。首先,扩展接口是由接口基(class Base1 : virtual public Base0)的虚拟继承定义的。其次,扩展实现是通过公开派生自Base1(用于接口)和私下派生自Impl0(用于实现):class Impl1 : public virtual Base1, private Impl0。我的问题如下:

(1) 如果扩展类中的函数在Impl0 中定义了需要公共数据的函数,我是否必须在“Impl1”中复制该数据?

(2) 有没有办法避免这种复制?

作为一个最小的例子,考虑一个实现四个基本算术函数的类层次结构:add()、substr()、mult() 和 div()。基本版本MathOps 包含 add() 和 subtr() 函数。扩展版本MathOps_Extn 包含 mult() 和 div()。上述技术给出了以下类层次结构。

#include<iostream>

using std::cout;
using std::endl;

class MathOps {
public:
  virtual int add(int x) = 0;
  virtual int subtr(int x) = 0;
};

class MathOps_Impl : public virtual MathOps {
private:
  int m_y;
public:
  MathOps_Impl(int y) : m_y(y) {
    cout << "MathOps_Impl initialized with value: " << m_y << endl;
  }

  virtual int add(int x) { return x + m_y;}
  virtual int subtr (int x) { return m_y - x;}
};

class MathOps_Extn  : public virtual MathOps {
  // Extends MathOps by adding mult() and div()                                                                                                                                 
public:
  virtual int mult(int x) = 0;
  virtual int div(int x) = 0;
};

class MathOps_Extn_Impl : public    virtual MathOps_Extn, private MathOps_Impl {
private:
  int m_y; // Have to replicate member data m_y here.                                                                                                                           
public:
  MathOps_Extn_Impl(int y) : MathOps_Impl(y), m_y(y) {
    cout << "MathOps_Extn_Impl initialized with value: " << m_y << endl;
  }

  virtual int mult(int x) {
    return x * m_y;
  }
  virtual int div(int x) {
    int quotient = x == 0? 0 : m_y/x;
    return quotient;
  }
};

int main() {                                                                                                                                           
  MathOps_Extn* B =  new MathOps_Extn_Impl(10);
  cout << "add 20: " << B->add(20) << endl;
  cout << "subtr 20: " << B->subtr(20) << endl;
  cout << "mult 2: " << B->mult(2) << endl;
  cout << "div 5: " << B->div(5) << endl;

注意m_yMathOps_Extn_Impl 中的复制。有什么办法可以避免这种复制吗?

【问题讨论】:

  • 谁愿意将算术运算融入分层设计?
  • 再走一步,拥有一个 MathOps_Extn_base?或者将 m_y 放入 MathOps
  • 你为什么不使用protected?但是,如果您想使用 private 修饰符进行继承,那么是的,您必须复制它。将m_y 放入接口而不将private 更改为protected 不会有太大变化。编辑:关于第一个问题,看看名称查找在 C++ 中是如何工作的。如果您将m_y 置于Impl0 中作为保护,那么您应该可以在Impl1 中使用它。
  • @PiotrJaszkowski 谢谢。在阅读关于 SO 的其他答案时,我得出了同样的结论(在发布我的问题之前我没有遇到过)。但是,不会让m_yprotected 允许从MathOps_Impl 派生的任何(客户端)类直接访问它(并因此破坏封装)?
  • @K-ballo 我希望你认真对待你的问题。这是解释我的问题的最小示例。

标签: c++ design-patterns


【解决方案1】:

注意MathOps_Extn_Impl 中m_y 的复制。有没有办法 避免这种复制?

是的。授予 MathOps_Impl::m_y protected 访问权限,而不是 private

您明确地问为什么派生类不能访问私有数据。这是设计使然。

【讨论】:

  • 我知道private 基类成员的不可访问性。但是,请参阅我对 PiotrJaszkowski 的评论,了解我对使用 protected 的不安。可能是protected 是要走的路,我只是觉得它打破了封装。
  • @RDK 我同意您的评论,即它可能会破坏封装。 K-ballo 还给出了一个有效的评论,即封装的设计不寻常。
【解决方案2】:

您可以通过公共实现类中的受保护成员函数在不破坏封装的情况下授予对公共数据的访问权限。

下面是免费的例子:)

#include <cstdio>

class Math
{
public:
    virtual ~Math() {}
    virtual int add(int b) const = 0;
};

class MoreMath : public virtual Math
{
public:
    virtual ~MoreMath() {}
    virtual int subtract(int b) const = 0;
};

class MathImpl : public virtual Math
{
private:
    int m_a;

public:
    MathImpl(int a) : m_a(a) {}
    virtual ~MathImpl() {}
    int add(int b) const { return m_a + b; }

protected:
    int value() const { return m_a; }
};

class MoreMathImpl : public virtual MoreMath, private MathImpl
{
public:
    MoreMathImpl(int a) : MathImpl(a) {}
    int subtract(int b) const { return value() - b; }
};

int main()
{
    MoreMath* one = new MoreMathImpl(1);
    printf("1 + 2 = %d\n", one->add(2));
    printf("1 - 2 = %d\n", one->subtract(2));
    delete one;

    return 0;
}

【讨论】:

  • 谢谢。我已接受您的回答,因为它避免了受保护的成员数据。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多