对象切片会破坏封装吗?

bol*_*lov 0 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
  }
};
Run Code Online (Sandbox Code Playgroud)

在此示例中,类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
  }
};
Run Code Online (Sandbox Code Playgroud)

乍一看一切都还好.Extended不修改Restricted或它的行为.Restricted仍然是相同的,我们只是添加另一种类型,也允许负数.

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

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

Restricted r = Extended{};

// or

Extended e;
e.set(-24);

Restricted r = e;

// and then:

r.do_something(); // oups
Run Code Online (Sandbox Code Playgroud)

有些东西没有加起来:

  • 创建Extended作为子类的错误是Restricted什么?如果是这样,为什么?
  • 被错设定aprotected,如果我希望a是永远非负?为什么?protected不应允许改变我班级的行为.
  • 是否C++允许对象切片是错误的?
  • 以上全部
  • 别的

Nic*_*las 6

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

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

由于违反(和因为你的好奇缺乏那种不变的virtual功能),Extended是打破OOP的基本规则:一个派生类的实例应该能够像(公共)基类的一个实例来对待.这并不意味着切片; 我的意思是,你应该能够传递Extended给一个带有指针/引用的函数Restricted,并且一切都应该像它正在与之交谈一样Extended.

创建Extended作为Restricted的子类是错误的吗?如果是这样,为什么?

这是错的:

  1. 使a protected代替private.

  2. Make Restricted的界面不是virtual.

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

没有.