对象切片会破坏封装吗?

时间:2016-09-12 15:14:13

标签: c++ oop inheritance encapsulation

我看到它的方式,在OOP中,公共界面的一个角色是确保对象始终处于有效状态(这是你能够memcpy的一个重要原因。到非pod类型)。例如。一个非常基本的,迂腐的例子:(请忽略类似java的set / get):

class Restricted {
protected:
  int a;

public:
  Restricted() : a{0} {}

  // always returns a non-negative number
  auto get() const -> int {
    return a;
  }

  auto set(int pa) -> void {
    if (pa < 0) 
      a = 0;
    else
      a = pa;
  }

  auto do_something() {
    // here I code with the assumption that
    // a is not negative
  }
};

在此示例中,类Restricted的建模方式使Restricted对象始终保持非负数。这就是我为Restricted定义有效状态的方法。通过查看界面,我可以说Restricted ::get将始终返回非负数。用户无法让Restricted保留负数。

a受到保护,可以让选项轻松扩展该类。因此,让我们使用允许所有数字的类型扩展Restricted

class Extended : public Restricted {
public:
  Extended()  { a = -1; }

  auto set(int pa) -> void {
    a = pa;
  }

  auto do_something() {
    // now a can be negative, so I take that into account
  }
};

乍一看一切都还好。 Extended并未修改Restricted或其行为。 Restricted仍然相同,我们只添加另一种允许负数的类型。

除了我们对Restricted的初始假设不再支持。用户可以轻松获取包含负数的Restricted对象(处于无效状态的对象),因为:

C++允许对象切片而不发出任何警告:

Restricted r = Extended{};

// or

Extended e;
e.set(-24);

Restricted r = e;

// and then:

r.do_something(); // oups

有些东西没有加起来:

  • Extended创建为Restricted的子类是不对的?如果是这样,为什么?
  • 如果我希望a始终为非负数,则将protected设为a是错误的吗?为什么? protected不允许改变我班级的行为。
  • C++允许对象切片是错误的吗?
  • 以上所有
  • 别的东西

2 个答案:

答案 0 :(得分:6)

  

如果我希望a总是非负的,那么设置为受保护是不对的?为什么呢?

是的,这是错的。 Restricted的界面要求 a为非负面。这是该类型设置的不变量。并且它没有virtual函数来允许派生类覆盖该不变量。

通过违反该不变量(以及由于您好奇缺少virtual函数),Extended打破了OOP的基本规则:派生类实例应该能够被视为一个实例一个(公共)基类。这并不意味着切片;我的意思是,你应该能够将一个Extended传递给一个带有Restricted指针/引用的函数,并且一切都应该像是在与Extended交谈一样。< / p>

  

将Extended创建为Restricted的子类是不对的?如果是这样,为什么?

错误地说:

  1. 制作a protected代替private

  2. Restricted的界面设为非virtual

  3.   

    C ++允许对象切片是不对的?

    没有

答案 1 :(得分:1)

C ++为你提供了一把枪,可以同时射击你的两只脚。情况一直如此。

如果要防止所描述的情况发生,请删除基类的复制构造函数,并让派生类的复制构造函数负责复制基类。

您也可以使用私有继承。

您可以选择最佳解决方案来满足您的班级设计要求。